現(xiàn)在存在有的時候發(fā)現(xiàn)回到桌面會重復打開閃屏頁,我研究了一下,有如下結果凭需。
重現(xiàn)方式:
用android的installer
安裝打開閃屏頁,按Home鍵回到首頁俄讹,然后點擊launcher的圖標會再打開一個閃屏頁,根據(jù)這篇博客是因為再打開時候Intent多了一個Flag,Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
那為什么會多這個flag弥奸,而又為什么多了這個flag只祠,會重復多打開頁面枯怖,這就是我這篇文章要講的勒庄。
解決方案還是上面講的
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)> 0) {
/**為了防止重復啟動多個閃屏頁面**/
finish();
return;
}
桌面launcher的打開與Installer打開的不同
先說結論,installer打開多了一個intent.setPackage(packageName)
代碼在Launcher3军熏,抽出來就是
public static void startAppByLauncher(Context context, String packageName) {
android.content.pm.PackageInfo pi = null;
try {
pi = context.getPackageManager().getPackageInfo(packageName, 0);
Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);
resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT) {
resolveIntent.setPackage(pi.packageName);
}
List<ResolveInfo> apps = context.getPackageManager().queryIntentActivities(resolveIntent, 0);
ResolveInfo ri = apps.iterator().next();
if (ri != null) {
String packageName1 = ri.activityInfo.packageName;
String className = ri.activityInfo.name;
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
ComponentName cn = new ComponentName(packageName1, className);
intent.setComponent(cn);
context.startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context.getApplicationContext(), "啟動失敗",
Toast.LENGTH_LONG).show();
}
}
而installer的打開在com.android.packageinstaller.installappprogress
public static void startAppByInstallApp(Context context, String packageName) {
try {
Intent intent= context.getPackageManager().getLaunchIntentForPackage(
packageName);
context.startActivity(intent);
} catch (Exception e) {
}
}
而getLaunchIntentForPackage的實際代碼在ApplicationPackageManager
@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);
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;
}
測試了一下轩猩,最后發(fā)現(xiàn)兩者的不同在于installappprogress多了一個intent.setPackage(packageName)。
那為什么多了一個intent.setPackage(packageName)會再此打開時導致多了Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
FLAG_ACTIVITY_BROUGHT_TO_FRONT是如何產(chǎn)生的
這是在 com.android.server.am.ActivityStackSupervisor
final ActivityStack lastStack = getLastStack();
ActivityRecord curTop = lastStack == null?
null : lastStack.topRunningNonDelayedActivityLocked(notTop);
if (curTop != null && (curTop.task != intentActivity.task ||
curTop.task != lastStack.topTask())) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (sourceRecord == null || (sourceStack.topActivity() != null &&
sourceStack.topActivity().task == sourceRecord.task)) {
// We really do want to push this one into the
// user's face, right now.
movedHome = true;
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity.
intentActivity.task.mOnTopOfHome = true;
}
targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
options = null;
}
}
關鍵在于 curTop.task != lastStack.topTask()
這個地方我估摸著是因為packageName會影響到task
FLAG_ACTIVITY_BROUGHT_TO_FRONT是如何起作用
這個我還沒找到實際代碼,只能看一下官方解釋
If, when starting the activity, there is already a task running that starts with this activity, then instead of starting a new instance the current task is brought to the front. The existing instance will receive a call to Activity.onNewIntent() with the new Intent that is being started, and with the Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set. This is a superset of the singleTop mode, where if there is already an instance of the activity being started at the top of the stack, it will receive the Intent as described there (without the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag set). See the Tasks and Back Stack document for more details about tasks.
這個FLAG其實是這個意思,比方說用A打開B界轩,此時在A的Intent中加上這個FLAG画饥,再在B中再啟動C,D浊猾,如果這個時候在D中再啟動B抖甘,這個時候最后的棧的情況是 A,C,D,B.
這篇文章有兩個地方?jīng)]解釋到,希望大家補充
- 為什么packageName為影響到curTop.task != lastStack.topTask()
- FLAG_ACTIVITY_BROUGHT_TO_FRONT是如何起作用的