Activity启动模式与任务栈(Task)全面深入记录(下)

转载 2017年10月13日 09:06:54

转载: 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);
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

清单文件代码如下:

<?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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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

当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的任务栈中。这就好比我们在路边收养了一只与主人走失了的猫,养着养着突然有一天,主人找上门来了,这只猫也就被带回去了。我们通过图解来更好地理解这种情景: 
 
  我们通过代码层面来验证一下,我们创建两个应用分别为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);
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
<activity android:name=".ActivityA">
     <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
</activity>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
<activity android:name=".ActivityC" android:exported="true"    
      android:allowTaskReparenting="true">
</activity>
  • 1
  • 2
  • 3

  我们通过AcitivityA启动B应用的ActivityC后,内存中栈的如下: 
 
  我们可以看到ActivityA和ActivityC同在一个栈中,接着我们回到桌面启动B应用,此时内存中的任务栈如下: 
 
  我们发现ActivityC从A应用的任务栈直接移动到B应用的任务栈,这也就符合我们前面所说的现象了。而当我们修改allowTaskReparenting为false时,再运行,然后重复上面的操作,查看内存中任务栈的变化: 
A应用启动B应用的ActivityC时 
 
  回到桌面再启动B应用时 
 
  对比发现,如果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的启动模式和任务栈都有相对清晰的了解了哈。 

Activity启动模式与任务栈(Task)全面深入记录(上)

转载请注明出处(谢谢): http://blog.csdn.net/javazejian/article/details/52071885任务栈简单入门  最近又把两本进阶书看了一遍,但总感觉好记...

Activity启动模式与任务栈(Task)全面深入记录

任务栈简单入门   最近又把两本进阶书看了一遍,但总感觉好记性不如烂笔头,所以还是决定通过博客记录一下,我们将分两篇来全面深入地记录Activity 启动模式与任务栈的内容。 androi...

Activity启动模式与任务栈(Task)全面深入记录(下)

转载请注明出处(谢谢): http://blog.csdn.net/javazejian/article/details/52072131  通过上一篇文件的分析,我们对Activity的启动模式...

Activity启动模式与任务栈(Task)

任务栈简单入门   最近又把两本进阶书看了一遍,但总感觉好记性不如烂笔头,所以还是决定通过博客记录一下,我们将分两篇来全面深入地记录Activity 启动模式与任务栈的内容。 androi...

完全理解android Activity启动模式LauchMode (深入Activity与任务栈)

之前笔者已经讲过了LauchMode的作用,以及尽量避开栈的概念使用GIF图片的方式尽可能简单地阐述了一下Activity的启动模式,这篇文章就再次深入,好好讲一下在各种启动模式下,Activity与...

Activity的启动模式,任务栈的切换

Activity的启动模式,任务栈的切换 1.Activity有四种启动模式:标准: standard 可以启动多个相同的Activity; 在当前Activity中可以通过点击按钮等方式,启动一...

任务栈,Activity的启动模式和运用场景

task stack    任务栈 : 1.一个应用程序都是由很多的activity 组成的。                  2.任务栈就是来存放用户开启的activity的。          ...

Activity任务栈与启动模式

一、Task与返回栈。 Task是指一系列的Activity,这些Activity按照各自的打开顺序排列在堆栈(返回栈)中。 按返回键可以从返回栈中弹出响应的Activity,直到返回主屏幕为止。当所...

Activity任务栈和启动模式

在android中,一个activity组件可以激活另一个activity组件(可能属于另一个应用程序)。     若新的被激活的activity组件属于另一个应用程序,则那个activity...

Activity启动模式和任务栈(3)

在软件设计中,在每个Activity中会有跳转到主页面的按钮. 但在实际使用中发现,任务栈中的数据不会弹出 会出现ABA这样的情况,网上查资料发现这样可以解决: 中的launchMode属性。它有四...
  • scyatcs
  • scyatcs
  • 2013年04月29日 21:05
  • 454
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Activity启动模式与任务栈(Task)全面深入记录(下)
举报原因:
原因补充:

(最多只允许输入30个字)