Android应用Launcher重复启动问题

作者: Ideaqjj | 来源:发表于2017-08-29 15:08 被阅读1342次

    Bug表现

    操作一:

    F_step1、使用Installer安装App,安装后选择打开app,应用进入闪屏页Activity,然后跳转到主页MianActivity;

    F_step2、在主页Activity时按下了Home键,回到桌面;

    F_step3、再点击app的icon图标,App并没有直接进入主页,而是先进入了闪屏页;

    操作二:

    S_step1、点击应用图标,应用进入闪屏页Activity,然后跳转到主页MainActivity;

    S_step2、在主页Activity时按下了Home键,回到桌面;

    S_step3、再点击app的icon图标,App并直接进入主页;

    很明显,操作一出现了Bug。

    原因查找

    操作一为何会出现这种情况?现在对这个过程进行跟踪定位:

    执行F_step1
    进入Welcome页面,此时WelcomeActivity生命周期正常,看看Task中的情况:

    在栈顶的Activity是welcome;

    接下来,进入到主要页面MainActivity

    整个生命周期走得毫无毛病,该怎么走怎么走,Task栈顶Activity是MainActivity;

    执行F_step2:

    执行之后程序退到后台,Task栈顶和之前的一样是MainActivity;

    执行F_step3:

    App再次启动了WelcomeActivity,并没有恢复退出时Task栈顶的Activit.

    接下来看看操作二的过程:

    ......

    结果,按home键推出后,再点击图标进入App,应用直接恢复了退出时栈顶的Activity,并没有重新启动WelcomeActivity;

    为何会这样?

    原因探究

    接下来,在WelcomeActivity中打印getIntent()和Task的信息;

    从Installer打开App:

    (1)打开应用:

    (2)按home键退出,再点击图标打开App

    从lancher打开App:

    (1)打开应用:

    (2)按home键退出,再点击图标打开App:

    因为WelcomeActivity并没有重新启动,所以没有打印Log信息。

    两者的区别:

    (1)从launcher启动,Flags比从Installer启动多了一个FLAG_ ACTIVITY_ RESET_ TASK_ IF_ NEEDED,添加此Flag的Task不会存在重复启动问题,保证Task的唯一性;

    (2)从Installer安装后打开应用,再按Home键退出后再进入,会多出一个FLAG_ ACTIVITY_ BROUGHT_ TO_ FRONT;

    由此可以看出,会发生重复启动是installer初次启动时并没有FLAG_ ACTIVITY_ RESET_ TASK_ IF _NEEDED;

    值得注意的是,执行操作一,新建的WelcomeActivity的TaskId和Pid始终没有发生改变,也就是说该Activity还是在原有的进程和任务栈中打开;

    解决方案:

    问题找到了,那就对症下药。将以下代码到onCreate()方法中:

    if (!this.isTaskRoot() && getIntent() != null) {
            String action = getIntent().getAction();
            if (getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) {
                finish();
                return;
            }
        } else {
            setContentView(R.layout.activity_welcome);
            goMainActivity();
        }
    

    这段代码是使用isTaskRoot()进行判断,判断当前Activity是否为根Activity,即应用启动的第一个Activity。如果不是,说明是重新实例化出来的,则finish()掉当前Activity,显示原有的Task;

    使用以上方法有个值得注意的地方:
    若Activity实现了finish() 和 onDestroy()方法,在方法中要保证无空对象操作等等,以免造成异常崩溃;

    到此该问题已经得到解决,然而...为何操作一会没有FLAG_ ACTIVITY_ RESET_ TASK_ IF_ NEEDED呢?

    原因继续探究

    查看installer的启动代码installappprogress https://android.googlesource.com/platform/packages/apps/PackageInstaller/+/47fe118e0178e9d72c98073ff588ee5cf353258e/src/com/android/packageinstaller/InstallAppProgress.java

      mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
                                mAppInfo.packageName);
    

    getLaunchIntentForPackage的代码在ApplicationPackageManager https://android.googlesource.com/platform/frameworks/base/+/483f3b06ea84440a082e21b68ec2c2e54046f5a6/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);
            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;
    }
    

    从代码中可以看到,installer启动只设置了Intent.FLAG_ ACTIVITY NEW TASK;

    再看launcher的启动设置 AppInfo https://android.googlesource.com/platform/packages/apps/Launcher3/+/master/src/com/android/launcher3/AppInfo.java

        public static Intent makeLaunchIntent(LauncherActivityInfo info) {
        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);
    }
    

    launcher中是有设置FLAG_ ACTIVITY_ RESET_ TASK_ IF_ NEEDED和FLAG_ ACTIVITY_ NEW_ TASK;

    所以,insataller安装没有设置FLAG_ ACTIVITY_ RESET_ TASK_ IF_ NEEDED。

    至于为何在installer中没有设置有FLAG_ ACTIVITY_ RESET_ TASK_ IF_ NEEDED,这确实还需要研究。

    相关文章

      网友评论

        本文标题:Android应用Launcher重复启动问题

        本文链接:https://www.haomeiwen.com/subject/qpiodxtx.html