欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

写不完的BUG---关于APP每次打开都走入口类的问题,,由于第一次打开该APP是

来源: 开发者 投稿于  被查看 11073 次 评论:130

写不完的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上面了,主要的判断逻辑存在于ActivityStarterstartActivityUnchecked()方法内。

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_TASKFLAG_ACTIVITY_RESET_TASK_IF_NEEDED(0x00200000),由于从后台转前台,再附加一个FLAG_ACTIVITY_BROUGHT_TO_FRONT(0x00400000)


作者:Luuu


用户评论