Activity 启动模式、常见启动Flag以及taskAffinity(版本升级偶尔不跳转安装界面的问题)

具体问题描述:版本升级时点击下载新版本,下载后安装apk,安装apk的代码按顺序依次执行,有时候可以唤起安装界面有时候不可以唤起安装界面。

                   唤起安装界面的的情况

                   1、非Android原生系统的手机可以正常唤起安装界面

                   2、当Android原生系统连接Android studio运行一次项目后升级可以正常唤起安装界面

                  3、当下载一次没有唤起安装界面,再次点击安装界面肯定会唤起安装界面    

/**
 * 安装apk
 */
public static void installApk(Context context, String apkPath) {
    File apkFile = new File(apkPath);
    if (!apkFile.exists()) {
        Log.e(TAG, "apk文件" + apkPath + "不存在");
        return;
    }
    Log.e(TAG, "apk文件" + apkPath + "存在");
    Intent installApkIntent = new Intent();
    installApkIntent.setAction(Intent.ACTION_VIEW);
    installApkIntent.addCategory(Intent.CATEGORY_DEFAULT);
    installApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_CLEAR_TASK);//将之前设置的flags修改为现在的这个样子可以正常唤起

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
        Log.e(TAG, "大于23版本");
        installApkIntent.setDataAndType(FileProvider.getUriForFile(context, context.getPackageName() + ".file_provider", apkFile),
                "application/vnd.android.package-archive");
        installApkIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    } else {
        Log.e(TAG, "小于23版本");
        installApkIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
    }
    if (CacheUtil.getContext().getPackageManager().queryIntentActivities(installApkIntent, 0).size() > 0) {
        Log.e(TAG, "启动安装的activity");
        context.startActivity(installApkIntent);
    }
}

参考资料:
1.http://m.blog.csdn.net/luoshengyang/article/details/6714543

2.http://blog.csdn.net/zhangjg_blog/article/details/10923643

前言

Android 开发者几乎都知道 Activity 有四种启动模式(standard,singleTop,singleTask,singleInstance)。当然,对于刚开始接触 Android 的开发者,可能一开始不了解或不理解,但是工作个一年两年以后,肯定会接触到这个知识点的。网上有太多太多这方面的文章,但是或多或少都不那么准确(当然也有准确的,只是我看到的文章有限……)。最近把四种启动模式,taskAffinity 属性及 FLAG_ACTIVITY_CLEAR_TOP,FLAG_ACTIVITY_NEW_TASK 和 FLAG_ACTIVITY_SINGLE_TOP 这三种Flag 放在一起,仔细的研究了一下,终于弄懂了它们之间错综复杂的关系。

什么是任务(Task)

首先,我们先来简单了解一下在 Android 应用程序中,任务(Task)是个什么样的概念。我们知道,Activity 是 Android 应用程序的四大组件之一,在应用程序运行时,用户为了完成某个功能而执行的一系列操作(页面跳转)就形成了一个 Activity 序列,这个序列在 Android 应用程序中就称之为任务,它是从用户体验的角度出发,把一组相关的 Activity 组织在一起而抽象出来的概念。

Task 是一组相互关联的 Activity 的集合,它是存在于 framework 层的一个概念,控制界面的跳转和返回。这个 Task 存在于一个称为 back stack的数据结构中,也就是说,framework 是以栈的形式管理用户开启的 Activity。这个栈的基本行为是,当用户在多个 Activity 之间跳转时,执行压栈操作,当用户按返回键时,执行出栈操作。

对初学者来说,在开发 Android 应用程序时,对任务的概念可能不是那么的直观,一般我们只关注如何实现应用程序中的每一个 Activity。事实上,Android 系统中的任务更多的是体现是应用程序运行的时候,因此,它相对于 Activity 来说是动态存在的,这就是为什么我们在开发时对任务这个概念不是那么直观的原因。

不同的应用的 Activity 可以在同一个任务里,也就是说 Task 是跨应用的,这样保证了用户操作的连贯性,用户体验比较好。不同任务之间切换,界面会闪一下,不连贯。

神奇的 taskAffinity

不过,我们在开发 Android 应用程序时,可以配置 Activity 的任务属性的,即告诉系统,这个 Activity 是要在新的任务中启动呢,还是在已有的任务中启动,亦或是其它的 Activity 能不能与这个 Activity 共享同一个任务,这个神奇的属性就是 taskAffinity。

在 AndroidManifest.xml ,我们通过属性

 

android:taskAffinity="july.com.tempdemo.xxx"

可以为每个Activity 设置其 taskAffinity。如果不设置,默认情况下,所有的 Activity 的taskAffinity 都一样,都为app的包名。

这里关于 taskAffinity 我们记住两点就可以:

    1. 一个任务(Task)的 affinity 由这个任务的根 Activity(root activity)的 taskAffinity 决定;
    1. 当 Activity 以FLAG_ACTIVITY_NEW_TASK 标志启动时,根据 taskAffinity 来决定它会被启动到哪个任务中。

三个Flag

Intent 类里定义了很多 FLAG,这里我只说三种:

  • FLAG_ACTIVITY_NEW_TASK
    仅仅通过intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);启动的 Activity 即使 task 里已经有实例了,依然会在栈顶创建一个新的实例。

  • FLAG_ACTIVITY_SINGLE_TOP
    仅仅通过intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);能够实现 singleTop 启动模式的效果,也就是仅在当前栈里判断实例是否处于栈顶,在栈顶就复用,否则新建一个实例。

  • FLAG_ACTIVITY_CLEAR_TOP
    清除目标 Activity上方所有的 Activity,如果目标 Activity 是 standard 的启动模式且启动时 intent 里没有设置FLAG_ACTIVITY_SINGLE_TOP这个flag,那么栈里的目标 Activity 也会被 finish 掉,重新创建一个实例。
    如果目标 Activity 是非 standard 的启动模式或者启动时设置了FLAG_ACTIVITY_SINGLE_TOP,就不会清除目标 Activity,执行其 onNewIntent() 方法。

Flag 组合设置:

  • 仅仅通过intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);这两行代码来启动的 Activity,如果栈里已有 Activity 的实例,那么清空此 Activity 及其以上的 activites,然后新建一个实例。

  • 仅仅通过intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);能够达到跟 singleTask 启动模式的效果,如果栈里已有实例,则清空其上的 activies,然后走 onNewIntent()。

Activity 的四种启动模式

我们在 AndroidManifest.xml 里为 Activity 设置 launchMode:

 

set launchMode.png

standard 启动模式

这是 Activity 默认的启动模式,这种模式下,每次 startActivity 都会在栈顶创建一个新的实例,在同一个任务中可以存在多个Activity 的实例。

singleTop 启动模式

栈顶复用,也就是说,要启动 singleTop 模式的 Activity,如果它恰好在当前栈顶,那么直接复用,执行其 onNewIntent 方法。否则,就重新创建一个实例入栈。

singleTask 启动模式

设置了"singleTask"启动模式的 Activity,在系统中只有一个实例,当再次启动该 Activity 时,会重用已存在的任务和实例,并且会调用这个实例的 onNewIntent()方法,将 Intent 实例传递到该实例中。

设置了"singleTask"启动模式的 Activity 的特点(划重点):
它在启动的时候,会先在系统(所有现存的tasks)中查找属性值 affinity 等于它的属性值 taskAffinity 的任务是否存在;

  • 如果存在这样的任务,那么继续在这个任务中查看是否已经存在相应的 Activity 实例。

    • 如果存在实例,则会复用这个任务跟实例,finish 掉它上面的所有的 activity ,使其处于栈顶,并执行其 onNewIntent() 方法。
    • 如果不存在实例,则在这个任务栈顶创建一个实例。
  • 如果不存这样的任务,那么就会以它的 taskAffinity 新创建一个任务,并在任务里创建一个实例。

因此,如果我们想要设置了 "singleTask" 启动模式的 Activity 在新的任务中启动,就要为它设置一个独立的 taskAffinity 属性值。

下面举几个例子,来体验一下singleTask 启动模式。测试 demo 下载地址:https://github.com/JulyDev/ActivityLaunchMode
ps: 这个 demo 改了很多次,想到什么情况就改一下,来验证自己的想法。因为随机组合条件太多了,不可能一一保存下来。所以最好自己写个 demo 亲自验证一下。

例子1. 有三个 Activity,MainActivity,SecondActivity, ThirdActivity, 其中SecondActivity设置为 singleTask ,其他都是standard启动模式,并且onClick事件里启动时也不加任何 Flag 或 Action。然后 MainActivity 启动SecondActivity,SecondActivity 再启动 ThirdActivity。

 

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            >
        </activity>

启动 app,依次打开这三个 Activity,结果如下:

 

singleTask only.png

 

可以发现,设置为 singleTask 的 SecondActivity,是跟 MainActivity 在同一个 task 里。为什么呢?因为都没有设置 taskAffinity 啊,默认都是包名,也就是相同的,那么启动 SecondActivity 时,根据 taskAffinity ,找到了MainActivity 所在的task,这里面是没有 SecondActivity 实例的,于是创建一个实例入栈。

例子2. 现在,在例子1的基础上,给 SecondActivity 设置一个不同的 taskAffinity :

 

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            android:taskAffinity="july.com.tempdemo.second"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            >
        </activity>

测试结果如下:

 

singleTask && taskAffinity.png

可以看到,此时 SecondActivity 新建了一个 task,不同于 MainActivity 所在的 task。
仔细看我们还发现,由 SecondActivity 启动的ThirdActivity 跟 SecondActivity 在同一个 task 里面。

singleInstance 启动模式

总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他 Activity 会自动运行于另一个任务中。当再次启动该 Activity 的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将 Intent 实例传递到该实例中。

设置了 singleInstance 的 Activity,整个系统只有一个实例,独占一个栈,且由它启动的 Activity 根据目标 Activity 的 taskAffinity 来选择进哪个 task,若不存在对应的 task,则新建一个 task 并新建一个目标 Activity 的实例入栈。
举例:
ThirdActivity 设置为 singleInstance 模式。其他都为 standard模式。MainActivity 启动 SecondActivity,SecondActivity 启动 ThirdActivity,ThirdActivity 又启动 SecondActivity。

 

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:launchMode="singleInstance"
            >
        </activity>
        

测试结果如下:

 

singleInstance.png

可以看到,ThirdActivity 单独在一个 task 里,而由 ThirdActivity(singleInstance) 启动的 SecondActivity,根据 affinity 找到了 MainActivity 所在的 task,并且直接在栈顶创建一个实例(不管栈里是否已经存在 SecondActivity 的实例)。这里跟 singleTask 模式不一样。设置了 singleTask 模式的 Activity, 启动的普通 Activity ,直接就放在当前栈顶了(见singleTask 启动模式一节的例子2)。

一、Activity的4种启动模式:
1、android:launchMode=”standard” 标准启动模式。这种启动模式也是Activity默认的,一个栈中可以有多个相同的Activity。不会考虑是否栈中是否有相同的Activity。比如设置A为standard模式,那么可以运行A-B-A-A-A;

2、android:launchMode=”singleTop” 单一栈顶启动模式, 又叫栈顶复用模式,栈顶只能存在一个相同的Activity。比如你栈顶是MainActivity.java,你想启动新的MainACtivity.java是无法实现的。但是栈中可以存在多个MainActivity.java实例。 比如设置A为singleTop模式,运行 A-B-A 这里栈中可以同时存在了两个A,但是如果A已经在栈顶了就不能在A之上再启动一个A,这时候虽然系统不会调用A的onCreate(),但会调用onNewIntent(),即位于栈顶的activity被复用。
当一个Activity已经在栈顶,但依然有可能启动它,而你又不想产生新的Activity实例,此时就可以用singleTop模式。

3、android:launchMode=”singleTask” 单一栈启动模式,又叫栈内复用模式,当使用这种启动模式的时候,栈中只能存在一个相同Activity的实例。比如设置A为singleTask模式,那么执行 A-B-C-D ,此时D在栈顶,你想启动一个A,此时会直接启动在栈底层的A,而不会去新建一个A,并且此时A已经成为了栈顶了,所以C和D都是被移出栈,也就是被销毁了,同理系统不会调用A的onCreate(),但会调用onNewIntent(),即栈内的activity被复用。
singleTask模式和前面两种模式的最大区别就是singleTask模式是任务内单例的,所以我们是否设定Activity为singleTask模式,就是看我们activity是否需要单例,例如你的某个Activity里面有一个列表,如果有多个实例,有可能导致用户看到的列表不一致,有的Activity需要经常启动,如果每次都创建实例,会导致占用资源过多,这些情况都可以使用singleTask模式(例如Launcher应用的activity就是singleTask模式的),但启动singleTask模式的Activity会导致任务栈内它上面的Activity被销毁,有可能会影响用户体验,使用时要注意。

4、android:launchMode=”singleInstance” 单例启动模式,singleInstance模式也是单例的,但和singleTask不同,singleTask只是任务栈内单例,系统里是可以有多个singleTask Activity实例的,而singleInstance Activity在整个系统里只有一个实例,启动一singleInstanceActivity时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。比如设置B为这种模式, 那么当依次启动A-B-C-D(此时D在栈顶),此时原始的栈中就有A、C、D这三个Activity,而B在一个新的栈中。此时当你按back键,你会发现是这样子的,D-C-A–B,这是因为DCA在先创建的(也就是主栈)中,所以当主栈中的Activity全部都移除栈外后, 才会轮到次栈,这个次栈中只有一个B。
把Activity独立于一个栈中,是为了别的程序访问此Activity,可以方便多个应用程序共享这个栈中的Activity。SingleInstance模式并不常用,如果我们把一个Activity设置为singleInstance模式,你会发现它启动时会慢一些,切换效果不好,影响用户体验。它往往用于多个应用之间,例如一个电视launcher里的Activity,通过遥控器某个键在任何情况可以启动,这个Activity就可以设置为singleInstance模式,当在某应用中按键启动这个Activity,处理完后按返回键,就会回到之前启动它的应用,不影响用户体验。

二、几种常见的启动Activity的FLAG
(1) FLAG_ACTIVITY_NEW_TASK
设置此状态,记住以下原则,首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性一样,所以下面的a情况会在同一个栈中,前面这句话有点拗口,请多读几遍),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity

a. 前提: Activity A和Activity B在同一个应用中. 
 
    操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B的Intent的Flag设为
             FLAG_ACTIVITY_NEW_TASK, Activity B被压入Activity A所在堆栈(堆栈状态: AB).

    原因: 默认情况下同一个应用中的所有Activity拥有相同的关系(taskAffinity).

 b. 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.

    操作1: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),

               在Activity A中启动Activity C, 启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,

               Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 长按Home键, 选择TaskA,

               Activity A回到前台, 再次启动Activity C(两种情况1.从桌面启动;2.从Activity A启动,两种情况一样), 这时TaskB回到前台, Activity C显示, 供用户使用, 即:

               包含FLAG_ACTIVITY_NEW_TASK的Intent启动Activity的Task正在运行, 则不会为该Activity创建新的Task,

               而是将原有的Task返回到前台显示.

    操作2: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),

               在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,

               Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C),  在Activity C中启动

               Activity D(TaskB的状态: CD) 长按Home键, 选择TaskA, Activity A回到前台, 再次启动Activity C(从桌面或者ActivityA启动,也是一样的),

               这时TaskB回到前台, Activity D显示,供用户使用.说明了在此种情况下设置FLAG_ACTIVITY_NEW_TASK后,会先查找是不是有Activity C存在的栈,
               
               根据亲和性(taskAffinity),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的顺序不变
(2) FLAG_ACTIVITY_CLEAR_TOP:
从字面意思上看是要将栈中所启动activity之上的所有activity清除,通过此flag启动时还是按照默认在本栈上启动,清除上面的activity,但是即使所启动的activity已存在,仍会销毁重新创建,如果结合single top 的flag则不会重新创建。

 前提: Activity A, Activity B, Activity C和Activity D在同一个应用中.

 操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 在Activity B中启动

         Activity C(堆栈状态: ABC), 在Activity C中启动Activity D(堆栈状态: ABCD), 在Activity D中启动Activity B,

         启动Activity B的Intent的Flag设置为FLAG_ACTIVITY_CLEAR_TOP, (堆栈状态: AB).

(3) FLAG_ACTIVITY_CLEAR_TASK:
必须配合new task使用,表示新启动的activity作为新栈的根activity。

(3) FLAG_ACTIVITY_BROUGHT_TO_FRONT:
 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.

 操作: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),

         在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,

         Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动

         Activity D(TaskB的堆栈状态: CD), 长按Home键, 选择TaskA, Activity A回到前台, 在Activity A中再次启动Activity C,

         在启动Activity C的Intent中设置Flag为FLAG_ACTIVITY_BROUGHT_TO_FRONT, TaskB回到前台,

         Activity C显示, (TaskB的堆栈状态: C).
(4) FLAG_ACTIVITY_MULTIPLE_TASK:
与FLAG_ACTIVITY_NEW_TASK结合使用, 首先在Intent中设置FLAG_ACTIVITY_NEW_TASK, 打开Activity,则启动一个新Task, 接着在Intent中设置FLAG_ACTIVITY_MULTIPLE_TASK, 再次打开同一个Activity,则还会新启动一个Task.

(5) FLAG_ACTIVITY_SINGLE_TOP:
当前Task堆栈中存在ABCD四个Activity, A是栈顶Activity, D为栈底Activity, 存在打开A的Inten中设置了FLAG_ACTIVITY_SINGLE_TOP标志, 则会使用栈顶A, 而不会从新New A.

(6) FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:
一般与FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET结合使用,如果设置该属性,这个activity将在一个新的task中启动或者或者被带到一个已经存在的task的顶部,这时这个activity将会作为这个task的首个页面加载。将会导致与这个应用具有相同亲和力的task处于一个合适的状态(移动activity到这个task或者从中移出),或者简单的重置这个task到它的初始状态

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:在当前的Task堆栈中设置一个还原点,当带有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求启动这个堆栈时(典型的例子是用户从桌面再次启动这个应用),还原点之上包括这个应用将会被清除。应用场景:在email程序中预览图片时,会启动图片观览的actvity,当用户离开email处理其他事情,然后下次再次从home进入email时,我们呈现给用户的应该是上次email的会话,而不是图片观览,这样才不会给用户造成困惑。

     例: 存在Activity A, Activity B, Activity C, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),

          在Activity A中启动Activity B(TaskA堆栈状态: AB), 接着Activity B启动Activity C(TaskA堆栈状态: ABC),

          启动Activity C的Intent中设置FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET标题, 这样TaskA中有一个还原点,

          当有包含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求TaskA堆栈时(比如请求Activity A)

          系统就会将还原点以上的Activity清除, TaskA堆栈中只剩下了AB.

三、taskAffinity属性:
(1) taskAffinity属性应和FLAG_ACTIVITY_NEW_TASK标志及allowTaskReparenting属性结合使用, 如果只使用taskAffinity属性,请参考上面Task默认的行为.

(2) 与FLAG_ACTIVITY_NEW_TASK标志结合:

 a. 前题: Activity A和Activity B在同一个应用中, Activity A与Activity B设置不同的taskAffinity属性.

    操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B

               的Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时系统会新开僻一个Task堆栈,TaskB(TaskB堆栈状态: B).

 b. 前题: Activity A在"TaskOne应用"中, Activity C在"TaskTwo应用"中, Activity A和ActivityC设置了相同的taskAffinity属性.

    操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity C, 启动Activity C的

               Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时Activity C会压入与Activity A堆栈相同的TaskA堆栈(TaskA堆栈状态: AC).
(3) 与allowTaskReparenting属性:

     例: 在"TaskOne应用"中有一个天气预报Activity A, Activity A与"TaskOne应用"中的其它Activity有默认的关系

          (taskAffinity属性都没有设置), 并且allowTaskReparenting属性设置为true, 现在存在一个"TaskTwo应用

          "启动了"TaskOne应用"中的天气预报Activity A,  这时Activity A与"TaskTwo应用"中的Activity在同一个Task,

          命名这个Task堆栈为TaskA, 这时"TaskOne应用"启动, 并且又打开发天气预报Activity A, 这时Activity A会从TaskA堆栈中转移到

          "TaskOne应用"所在的堆栈, 即Activity A可以在多个堆栈中来回转移.       

四、 alwaysRetainTaskState属性:
如果Task堆栈中的Root Activity设置了此属性值为true, 不管出现任何情况, 一直会保留Task栈中Activity的状态.

五、 clearTaskOnLaunch属性:
如果Task堆栈中的Root Activity设置了此属性值为true, 只要你一离开这个Task栈, 则系统会马上清理除了Root Activity的全部Activity.

六、 finishOnTaskLaunch属性:
如果某Activity设置了finishOnTaskLaunch属性, 只要你一离开这个Task栈, 则系统会马上清除这个Activity,不管这个Activity在堆栈的任何位置.

七、startActivityForResult相关的一些坑
正因为activity的很多启动模式或flag具有清除栈内已有activity的效果,清除实际是调用的系统的remove task方法,该方法会使得被清除的activity 执行onDestory等方法销毁,同时如果被销毁的activity是被其他activity 用startActivityForResult方法启动的,销毁时会给它的启动activity传递回去result cancel 事件,启动activity 的onActivityResult方法会被调用,这个时候容易出现我们意料之外的问题。

例如:A是一个桌面应用的入口 activity,并设为了singleTask 启动模式的,我在A中用startActivityForResult方法启动了B,其中有个逻辑是如果在A的的onActivityResult方法中回调了 Result cancel事件,A activity调用finish销毁。首次点击A,然后在A中用startActivityForResult启动B,这里没问题,然后home键回桌面,再次点击应用图标启动,此时发现没有反应什么界面都没出现,看现象还以为应用异常闪退。实际是由于A是singleTask 启动模式,点图标启动A时要清除A所在栈上面的所有activity,栈中B被remove task 然后onDestory, 栈中原来的A收到onActivityResult事件,然后执行finish也销毁了,然后就因为没有activity存在了不显示任何界面。

从Task的角度看,Android认为不同Task之间的Activity是不能传递数据的,因此如果启动用startActivityForResult方式启动新activity,而新启动的activity和当前activity不在同一个栈时,当前activity的onActivityResult方法回马上被回调,并且传递回result cancel事件,可以从AMS打出的log上看到有这么一句:
“WARN/ActivityManager(67): Activity is launching as a new task, so cancelling activity result.” 就是这个意思,下面startActivityForResult的注释也进行了说明。

因此对于启动模式是singleInstance或singleTask,或者是new task flag标志启动的情况要尤其注意下此问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值