美文网首页四大组件
TaskAffinity应用场景【转】

TaskAffinity应用场景【转】

作者: HarveyLegend | 来源:发表于2019-02-27 09:12 被阅读0次

    转载请注明出处(谢谢):
    http://blog.csdn.net/javazejian/article/details/52072131

    通过上一篇文件的分析,我们对Activity的启动模式有了比较清晰的了解后,本篇我们将继续对Activity启动模式的相关参数和任务栈分析,接下来我们就继续上一篇的问题,如何通过taskAffinity属性在同一个应用中创建多个任务栈进行探究。

    任务栈之taskAffinity属性

    TaskAffinity特点如下:

    • TaskAffinity 参数标识着Activity所需要的任务栈的名称,默认情况下,一个应用中所有Activity所需要的任务栈名称都为该应用的包名。
    • TaskAffinity 属性一般跟singleTask模式或者跟allowTaskReparenting属性结合使用,在其他情况下没有实际意义。
    • TaskAffinity属性的值不能与当前应用包名相同,否则其值跟作废没两样。

    TaskAffinity和singleTask启动模式结合使用

    当TaskAffinity和singleTask启动模式结合使用时,当前Activity的任务栈名称将与TaskAffinity属性指定的值相同,下面我们通过代码来验证,我们同过MainActivity来启动ActivityA,其中MainActivity启动模式为默认模式,ActivityA启动模式为singleTask,而TaskAffinity属性值为android:taskAffinity="com.zejian.singleTask.affinity" MainActivity和ActivityA代码如下:

    
    package comzejian.myapplication;
     
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
     
    public class MainActivity extends AppCompatActivity {
     
        private Button btn;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btn= (Button) findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent i = new Intent(MainActivity.this,ActivityA.class);
                    startActivity(i);
                }
            });
        }
    }
    

    ActivityA.class 代码如下:

    
    package comzejian.myapplication;
     
    import android.app.Activity;
    import android.os.Bundle;
     
    /**
     * Created by zejian
     * Time 16/7/26.
     * Description:
     */
    public class ActivityA extends Activity{
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_a);
        }
    }
    

    清单文件代码如下:

    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="comzejian.myapplication">
     
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <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=".ActivityA"
                        android:launchMode="singleTask"
                        android:taskAffinity="com.zejian.singleTask.affinity"
                />
     
        </application>
    </manifest>
    

    现在我们启动MainActivity,后再启动ActivityA,然后我们通过 adb shell dumpsys activity activities 命令查看此时栈的情况:

    image

    我们可以清楚地看到两个任务栈,其中一个是id=249,栈名称为com.zejian.singleTask.affinity的任务栈,该栈包含了ActivityA,另外一个则是id=248,栈名为默认包名的任务栈,包含了MainActivity。到此我们也明白了,我们确实可以通过singleTask与android:taskAffinity属性相结合的方式来指定我们Activity所需要的栈名称,使相应的Activity存在于不同的栈中,图解如下:


    image

    当TaskAffinity和allowTaskReparenting结合使用

    首先我们来聊聊allowTaskReparenting属性,它的主要作用是activity的迁移,即从一个task迁移到另一个task,这个迁移跟activity的taskAffinity有关。当allowTaskReparenting的值为“true”时,则表示Activity能从启动的Task移动到有着affinity的Task(当这个Task进入到前台时),当allowTaskReparenting的值为“false”,表示它必须呆在启动时呆在的那个Task里。如果这个特性没有被设定,元素(当然也可以作用在每次activity元素上)上的allowTaskReparenting属性的值会应用到Activity上。默认值为“false”。这样说可能还比较难理解,我们举个例子,比如现在有两个应用A和B,A启动了B的一个ActivityC,然后按Home键回到桌面,再单击B应用时,如果此时,allowTaskReparenting的值为“true”,那么这个时候并不会启动B的主Activity,而是直接显示已被应用A启动的ActivityC,我们也可以认为ActivityC从A的任务栈转移到了B的任务栈中。这就好比我们在路边收养了一只与主人走失了的猫,养着养着突然有一天,主人找上门来了,这只猫也就被带回去了。我们通过图解来更好地理解这种情景:

    image

    我们通过代码层面来验证一下,我们创建两个应用分别为ActivityTask(简称A应用)和ActivityTask2(简称B应用),其中A包含ActivityA,B包含ActivityC,我们通过ActivityA启动B应用中的ActivityC,再回到桌面,启动B应用,此时我们观察A应用和B应用各自栈的变化(因为A,B为不同的应用所以taskAfinity属性值肯定不同,所以这里我们就没必要指定了)。
    ActivityA及其清单文件代码如下:

    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
     
    /**
     * Created by zejian
     * Time 16/7/23.
     * Description:
     */
    public class ActivityA extends Activity {
     
        private Button btnC;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_a);
     
            btnC= (Button) findViewById(R.id.mainC);
            btnC.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(Intent.ACTION_MAIN);
                    intent.addCategory(Intent.CATEGORY_LAUNCHER);
                    ComponentName cn = new ComponentName("com.cmcm.activitytask2", "com.cmcm.activitytask2.ActivityC");
                    intent.setComponent(cn);
                    startActivity(intent);
                }
            });
        }
    }
    
    
    <activity android:name=".ActivityA">
         <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
    </activity>
    

    ActivityA及其清单文件代码如下:

    
    package com.cmcm.activitytask;
     
    package com.cmcm.activitytask;
     
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
     
    /**
     * Created by zejian
     * Time 16/7/23.
     * Description:
     */
    public class ActivityC extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_c);
        }
    }
    
    
    <activity android:name=".ActivityC" android:exported="true"    
          android:allowTaskReparenting="true">
    </activity>
    

    我们通过AcitivityA启动B应用的ActivityC后,内存中栈的如下:


    image

    我们可以看到ActivityA和ActivityC同在一个栈中,接着我们回到桌面启动B应用,此时内存中的任务栈如下:


    image

    我们发现ActivityC从A应用的任务栈直接移动到B应用的任务栈,这也就符合我们前面所说的现象了。而当我们修改allowTaskReparenting为false时,再运行,然后重复上面的操作,查看内存中任务栈的变化:
    A应用启动B应用的ActivityC时


    image

    回到桌面再启动B应用时


    image

    对比发现,如果allowTaskReparenting值为false时,ActivityC并不会直接从A应用的任务栈迁移到B应用的任务栈,而是B应用直接重新创建了ActivityC的实例。到此我们对于allowTaskReparenting和taskAffinity属性的了解就已经相当深入了,不过有点需要说明的是allowTaskReparenting仅限于singleTop和standard模式,这是因为一个activity的affinity属性由它的taskAffinity属性定义(代表栈名),而一个task的affinity由它的root activity定义。所以,一个task的root activity总是拥有和它所在task相同的affinity。由于以singleTask和singleInstance启动的activity只能是一个task的root activity,因此allowTaskReparenting仅限于以standard 和singleTop启动的activity,大家可以自行测试一下,这里我们就不测试了哈,下面我们再来说说它们可能应用用场景。

    TaskAffinity与allowTaskReparenting和singleTask结合时可能发生的应用场景

    • TaskAffinity与singleTask应用场景

    假如现在有这么一个需求,我们的客户端app正处于后台运行,此时我们因为某些需要,让微信调用自己客户端app的某个页面,用户完成相关操作后,我们不做任何处理,按下回退或者当前Activity.finish(),页面都会停留在自己的客户端(此时我们的app回退栈不为空),这显然不符合逻辑的,用户体验也是相当出问题的。我们要求是,回退必须回到微信客户端,而且要保证不杀死自己的app.这时候我们的处理方案就是,设置当前被调起Activity的属性为:

    LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"

    其中com.tencent.mm是借助于工具找到的微信包名,就是把自己的Activity放到微信默认的Task栈里面,这样回退时就会遵循“Task只要有Activity一定从本Task剩余Activity回退”的原则,不会回到自己的客户端;而且也不会影响自己客户端本来的Activity和Task逻辑。

    • TaskAffinity与allowTaskReparenting应用场景

    一个e-mail应用消息包含一个网页链接,点击这个链接将出发一个activity来显示这个页面,虽然这个activity是浏览器应用定义的,但是activity由于e-mail应用程序加载的,所以在这个时候该activity也属于e-mail这个task。如果e-mail应用切换到后台,浏览器在下次打开时由于allowTaskReparenting值为true,此时浏览器就会显示该activity而不显示浏览器主界面,同时actvity也将从e-mail的任务栈迁移到浏览器的任务栈,下次打开e-买了时并不会再显示该activity
      到此TaskAffinity就全部介绍完了,最后我们再来了解几个跟任务栈相关的属性参数;

    清空任务栈

    Android系统除了给我提供了TaskAffinity来指定任务栈名称外,还给我提供了清空任务栈的方法,在一般情况下我们只需要在<activity>标签中指明相应的属性值即可。

    • android:clearTaskOnLaunch

    这个属性用来标记是否从task清除除根Activity之外的所有的Activity,“true”表示清除,“false”表示不清除,默认为“false”。这里有点我们必须要注意的,这个属性只对任务栈内的root Activity起作用,任务栈内其他的Activity都会被忽略。如果android:clearTaskOnLaunch属性为“true”,每次我们重新进入这个应用时,我们只会看到根Activity,任务栈中的其他Activity都会被清除出栈。
      比如一个应用的Activity A,B,C,其中clearTaskOnLaunch设置为true,C为默认值,我们依次启动A,B,C,点击HOME,再在桌面点击图标。启动的是A,而B,C将都被移除当前任务栈。也就是说,当Activity的属性clearTaskOnLaunch为true时将被优先启动,其余的Activity(B、C)都被移除任务栈并销毁,除非前面A已经finish销毁,后面的已注册clearTaskOnLaunch为true的activity(B)才会生效。
      特别地,如果我们的应用中引用到了其他应用的Activity,这些Activity设置了android:allowTaskReparenting属性为“true”,则它们会被重新宿主到有共同affinity的task中。

    • android:finishOnTaskLaunch

    finishOnTaskLaunch属性与clearTaskOnLaunch 有些类似,它们的区别是finishOnTaskLaunch是作用在自己身上(把自己移除任务栈,不影响别的Activity),而clearTaskOnLaunch则是作用在别人身上(把别的Activity移除任务栈),如果我们把Activity的android:finishOnTaskLaunch属性值设置为true时,离开这个Activity所依赖的任务栈后,当我们重新返回时,该Activity将会被finish掉,而且其他Activity不会受到影响。

    • android:alwaysRetainTaskState

    alwaysRetainTaskState实际上是给了当前Activity所在的任务栈一个“免死金牌”,如果当前Activity的android:alwaysRetainTaskState设置为true时,那么该Activity所在的任务栈将不会受到任何清理命令的影响,一直保持当前任务栈的状态。

    好了,到此本篇也就完结,相信通过两篇的记录我们对Activity的启动模式和任务栈都有相对清晰的了解了哈。

    相关文章

      网友评论

        本文标题:TaskAffinity应用场景【转】

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