写不完的BUG---关于APP每次打开都走入口类的问题,,由于第一次打开该APP是
写不完的BUG---关于APP每次打开都走入口类的问题,,由于第一次打开该APP是
最近完成了一个新的项目,在测试的时候发现APP在启动页之后键入首页,按home键进入后台之后,在桌面再次点击进入该APP的时候,又重新开始走启动页的流程,开始以为是后台已经被"杀死"了,但是从后台进入该APP又是正常的,如果把后台APP清除掉,重新在桌面打开,一切都没问题。
由于第一次打开该APP是在安装好APP后,使用系统的安装器打开APP的,进入后台之后再去点击桌面,那么问题很可能的原因是出自于他们的Intent不一致导致的。
public boolean filterEquals(Intent other) { if (other == null) { return false; } if (!Objects.equals(this.mAction, other.mAction)) return false; if (!Objects.equals(this.mData, other.mData)) return false; if (!Objects.equals(this.mType, other.mType)) return false; if (!Objects.equals(this.mPackage, other.mPackage)) return false; if (!Objects.equals(this.mComponent, other.mComponent)) return false; if (!Objects.equals(this.mCategories, other.mCategories)) return false; return true; }
从上面的图和代码可以分析得出,两者Intent的package不一致导致了它们Intent是不一样的,因为不一致就又导致了需要start的Activity再次被添加到Task上面了,主要的判断逻辑存在于ActivityStarter
的startActivityUnchecked()
方法内。
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) { //省略其余代码 ... ActivityRecord reusedActivity = getReusableIntentActivity(); if (reusedActivity != null) { ... reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity); ... setTaskFromIntentActivity(reusedActivity); //如果满足条件就直接返回,下面的方法都将不会被执行了 if (!mAddingToTask && mReuseTask == null) { resumeTargetStackIfNeeded(); if (outActivity != null && outActivity.length > 0) { outActivity[0] = reusedActivity; } return START_TASK_TO_FRONT; } } }
这里主要的操作是获取一个可以复用的Task的栈顶的Activity,将该Task移动到前台,具体的分析可以看这里,重点看setTaskFromIntentActivity
方法。
private void setTaskFromIntentActivity(ActivityRecord intentActivity) { if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) { //若设置了该Flag,则清空Task,设置Intent final TaskRecord task = intentActivity.getTask(); task.performClearTaskLocked(); mReuseTask = task; mReuseTask.setIntent(mStartActivity); mMovedOtherTask = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || mLaunchSingleInstance || mLaunchSingleTask) { ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity, mLaunchFlags); if (top == null) { mAddingToTask = true; mStartActivity.setTask(null); mSourceRecord = intentActivity; final TaskRecord task = mSourceRecord.getTask(); if (task != null && task.getStack() == null) { mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */, null /* bounds */, mLaunchFlags, mOptions); mTargetStack.addTask(task, !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } } } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) { //此时重点看这个判断里面代码,当前未设置FLAG_ACTIVITY_SINGLE_TOP,所以走else方法 if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) && intentActivity.realActivity.equals(mStartActivity.realActivity)) { if (intentActivity.frontOfTask) { intentActivity.getTask().setIntent(mStartActivity); } deliverNewIntent(intentActivity); } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { mAddingToTask = true; mSourceRecord = intentActivity; } } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { mAddingToTask = true; mSourceRecord = intentActivity; } else if (!intentActivity.getTask().rootWasReset) { intentActivity.getTask().setIntent(mStartActivity); } }
在该方法内,isSameIntentFilter()
最后是调用到Intent的filterEquals()
方法,就是文章开始贴出来的那段代码,假如Intent的相同,那么mAddingToTask
依旧为false且mReuseTask也是为null,所以直接return START_TASK_TO_FRONT
,造成的效果就是把后台的Task移动前台;那么此时判断的Intent是不相同的,它将会继续走下面的将Activity添加到Task上的操作,就相当于前面所说的,又开始走启动页流程了,因为现在Task的栈顶Activity就是启动页Activity。
由于偷懒没有去找系统的安装器的源码,所以使用下面的代码复现了一下这个问题
Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage("xxxxxx"); if (intent != null) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); }
在getLaunchIntentForPackage可以看到给Intent设置了Package
ApplicationPackageManager.java @Override public Intent getLaunchIntentForPackage(String packageName) { 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; }
所以解决办法就是就是手动将package设置为null,如果是不能直接修改的,则可以在启动页判断目前Activity的isTaskRoot,出现问题的情况下栈内的情况应该是A-B-A,此时把A finish了,就变成正常的A-B。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (!isTaskRoot) { finish() return } }
下面是无关紧要的备注
从文章最开始的图片可以看到,第一个Intent的flag为0x10600000,第二个Intent的flag为0x10000000,开始的时候直接是系统启动器打开APP的,所以系统启动器打开APP附加FLAG_ACTIVITY_NEW_TASK
,桌面点击启动的时候会附加FLAG_ACTIVITY_NEW_TASK
、FLAG_ACTIVITY_RESET_TASK_IF_NEEDED(0x00200000)
,由于从后台转前台,再附加一个FLAG_ACTIVITY_BROUGHT_TO_FRONT(0x00400000)
作者:Luuu
用户评论