• In-application responseandroid.intent.action.MAINandroid.intent.category.LAUNCHERReferred to in this article as the main interface.
  • This article is based on Android O

Phenomenon of the problem

Install the application using the PackageInstaller. On the installation completion screen, click Open. After the application screen is opened, press the Home button to return to the desktop, and tap the application icon on the desktop.

Problem: Open another flash screen.

Question why

Applications start another application, the above problem scenarios using PackageManager# getLaunchIntentForPackage () this API, its implementation is:

// frameworks\base\core\java\android\app\ApplicationPackageManager.java
@Override
public Intent getLaunchIntentForPackage(String packageName) {
    // First see if the package has an INFO activity; the existence of
    // such an activity is implied to be the desired front-door for the
    // overall package (such as if it has multiple launcher entries).
    Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
    intentToResolve.addCategory(Intent.CATEGORY_INFO);
    intentToResolve.setPackage(packageName);
    List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);

    // Otherwise, try to find a main launcher activity.
    if (ris == null || ris.size() <= 0) {
        // reuse the intent instance
        intentToResolve.removeCategory(Intent.CATEGORY_INFO);
        intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
        intentToResolve.setPackage(packageName); / / < - here
        ris = queryIntentActivities(intentToResolve, 0);
    }
    if (ris == null || ris.size() <= 0) {
        return null;
    }
    Intent intent = new Intent(intentToResolve);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(ris.get(0).activityInfo.packageName,
            ris.get(0).activityInfo.name);
    return intent;
}
Copy the code

The implementation of starting an application on a normal desktop is as follows:

// Launcher3\src\com\android\launcher3\AppInfo.java
public static Intent makeLaunchIntent(Context context, LauncherActivityInfoCompat info, UserHandleCompat user) {
    long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
    return new Intent(Intent.ACTION_MAIN)
        .addCategory(Intent.CATEGORY_LAUNCHER)
        .setComponent(info.getComponentName())
        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
        .putExtra(EXTRA_PROFILE, serialNumber);
}
Copy the code

Compared the above two kinds of start another application code, can be found: PackageManager# getLaunchIntentForPackage (). This API is much intentToResolve setPackage (packageName); .

FLAG_ACTIVITY_BROUGHT_TO_FRONT: intEnt_activity_brought_to_front: intent_activity_brought_to_front: intent_activity_brought_to_front: intent_activity_brought_to_front: intent_activity_brought_to_front: intent_activity_brought_to_front

// frameworks\base\services\core\java\com\android\server\am\ActivityStarter.java
/**
 * Figure out which task and activity to bring to front when we have found an existing matching
 * activity record in history. May also clear the task if needed.
 * @param intentActivity Existing matching activity.
 * @return {@link ActivityRecord} brought to front.
 */
private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
    mTargetStack = intentActivity.getStack();
    mTargetStack.mLastPausedActivity = null;
    // If the target task is not in the front, then we need to bring it to the front...
    // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
    // the same behavior as if a new instance was being started, which means not bringing it
    // to the front if the caller is not itself in the front.
    final ActivityStack focusStack = mSupervisor.getFocusedStack();
    ActivityRecord curTop = (focusStack == null)?null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);

    finalTaskRecord topTask = curTop ! =null ? curTop.getTask() : null;
    if(topTask ! =null&& (topTask ! = intentActivity.getTask() || topTask ! = focusStack.topTask()) && ! mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);/ / < - here
        if (mSourceRecord == null|| (mSourceStack.topActivity() ! =null &&
                mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) {
            // We really do want to push this one into the user's face, right now.
            if(mLaunchTaskBehind && mSourceRecord ! =null) {
                intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
            }
            mMovedOtherTask = true;
// ...
}
Copy the code

If the B application starts the backend, objects in the task stack will be created or reused according to the lauchMode of the main interface of the B application, and unexpected results will be expected. For example, if B’s main interface is Standard, a second main interface will be created in the task stack.

For more detailed analysis, please refer to the article: On the Android application returning to the desktop will repeatedly open the splash screen page

The solution

The first kind of

To initiate A jump in initial application A:

Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
intent.setPackage(null); // Add this code
context.startActivity(intent);
Copy the code

The second,

In the target application B’s main screen onCreate, add:

super.onCreate(savedInstanceState);
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)> 0) {
    /** To prevent repeated startup of multiple splash pages **/
    finish();
    return;
}
Copy the code