- In-application response
android.intent.action.MAIN
和android.intent.category.LAUNCHER
Referred 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