Activity启动模式与TaskAffinity属性

Activity的四种启动模式如下:

standard、singleTop、singleTask、singleInstance

我们一边讲理论一边结合案例来全面学习这四种启动模式。 为了打印方便,定义一个基础BaseActivity,在其onCreate方法和onNewIntent方法中打印出当前Activity的日志信息,主要包括所属的task,当前类的hashcode,之后我们进行测试的Activity都直接继承该BaseActivity

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("chric", "*****onCreate()方法******");
        Log.i("chric", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
        dumpTaskAffinity();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.i("chric", "*****onNewIntent()方法*****");
        Log.i("chric", "onNewIntent:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
        dumpTaskAffinity();
    }

    protected void dumpTaskAffinity(){
        try {
            ActivityInfo info = this.getPackageManager()
                 .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
            Log.i("chric", "taskAffinity:"+info.taskAffinity);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

standard-默认模式

这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。

配置形式:
<activity android:name=".SecondActivity" android:launchMode="standard"/>
使用案例:

对于standard模式,android:launchMode可以不进行声明,因为默认就是standard。 SecondActivity的代码如下,入口MainActivity中有一个按钮进入该Activity,这个Activity中又有一个按钮启动SecondActivity。

public class MainActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button btn_standard= (Button)   findViewById(R.id.btn_standard);
    btn_standard.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       Intent intent = new Intent(MainActivity.this, SecondActivity.class);
       startActivity(intent);
    }
   });
        Log.i("chric", "*****onCreate()方法******");
        Log.i("chric", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}
public class SecondActivity extends BaseActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        jump= (Button) findViewById(R.id.button3);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              Intent intent = new Intent(SecondActivity.this, SecondActivity.class);
              startActivity(intent);
            }
        });
    }

}
测试方式:

MainActivity --> SecondActivity --> SecondActivity --> SecondActivity --> SecondActivity

输出的日志如下:
MainActivity TaskId: 2 hasCode:1249333352
SecondActivity TaskId: 2 hasCode:1249526392
SecondActivity TaskId: 2 hasCode:1249424816
SecondActivity TaskId: 2 hasCode:1249439692
SecondActivity TaskId: 2 hasCode:1249459968
测试结果:

1)日志输出了四次SecondActivity的和一次MainActivity的,并且所属的任务栈的id都是2,这也验证了谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中这句话.因为启动SecondActivity的是MainActivity,而MainActivity的taskId是2,因此启动的SecondActivity也应该属于id为2的这个task,后续的3个SecondActivity是被SecondActivity这个对象启动的,因此也应该还是2,所以taskId都是2。

2)并且每一个Activity的hashcode都是不一样的,说明他们是不同的实例,即“每次启动一个Activity都会重写创建一个新的实例”

singleTop模式

这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。需要注意的是这个Activity它的onCreate(),onStart()方法不会被调用,因为它并没有发生改变。

配置形式:
<activity android:name=".SingleTopActivity" android:launchMode="singleTop"/>
<activity android:name=".OtherTopActivity" android:launchMode="singleTop"/>
使用案例:
public class SingleTopActivity extends BaseActivity {
    private Button jump, jump2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_singletop);

        jump = (Button) findViewById(R.id.button);
        jump2 = (Button) findViewById(R.id.button2);
        jump.setOnClickListener(new View.OnClickListener() {
       @Override
        public void onClick(View v) {
        Intent intent = new Intent(SingleTopActivity.this, SingleTopActivity.class);
        startActivity(intent);
        }
      });
       jump2.setOnClickListener(new View.OnClickListener() {
       @Override
        public void onClick(View v) {
          Intent intent = new Intent(SingleTopActivity.this, OtherTopActivity.class);
          startActivity(intent);
          }
       });
        Log.i("chric", "*****onCreate()方法******");
        Log.i("chric", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}

public class OtherTopActivity extends AppCompatActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other);

        jump= (Button) findViewById(R.id.btn_other);
        jump.setOnClickListener(new View.OnClickListener() {
      @Override
       public void onClick(View v) {
         Intent intent = new Intent(OtherTopActivity.this, SingleTopActivity.class);
         startActivity(intent);
            }
        });
        Log.i("chric", "*****onCreate()方法******");
        Log.i("chric", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}
   测试方式:

   操作和standard模式类似,直接贴输出日志

onCreate:MainActivity TaskId: 3 hasCode:1249332216
onCreate:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntent:SingleTopActivity TaskId: 3 hasCode:1249464444
测试结果:

1)除了第一次进入SingleTopActivity这个Activity时,输出的是onCreate方法中的日志,后续的都是调用了onNewIntent方法,并没有调用onCreate方法,并且四个日志的hashcode都是一样的

2)说明栈中只有一个实例。这是因为第一次进入的时候,栈中没有该实例,则创建,后续的三次发现栈顶有这个实例,则直接复用,并且调用onNewIntent方法。

3)那么假设栈中有该实例,但是该实例不在栈顶情况又如何呢? 我们先从MainActivity中进入到SingleTopActivity,然后再跳转到OtherActivity中,再从OtherActivity中跳回SingleTopActivity,再从SingleTopActivity跳到SingleTopActivity中,看看整个过程的日志。

输出的日志如下:
onCreate:SingleTopActivity TaskId: 4 hasCode:1249520904
onCreate:OtherTopActivity TaskId: 4 hasCode:1249420244
onCreate:SingleTopActivity TaskId: 4 hasCode:1249448776
onNewIntent:SingleTopActivity TaskId: 4 hasCode:1249448776
测试结果:

1)从MainActivity进入到SingleTopActivity时,新建了一个SingleTopActivity对象。

2)从SingleTopActivity跳到OtherActivity时,新建了一个OtherActivity,此时task中存在三个Activity,从栈底到栈顶依次是MainActivity,SingleTopActivity,OtherActivity。

3)此时如果再跳到SingleTopActivity,即使栈中已经有SingleTopActivity实例了,但是依然会创建一个新的SingleTopActivity实例,这一点从上面的日志的hashCode可以看出,此时栈顶是SingleTopActivity,如果再跳到SingleTopActivity,就会复用栈顶的SingleTopActivity,即会调用SingleTopActivity的onNewIntent方法。这就是上述日志的全过程。

对以上内容进行总结:

standard启动模式是默认的启动模式,每次启动一个Activity都会新建一个实例不管栈中是否已有该Activity的实例。

singleTop模式况分3种情况:

1)当前栈中已有该Activity的实例并且该实例位于栈顶时,不会新建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewIntent方法

2)当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例

3)当前栈中不存在该Activity的实例时,其行为同standard启动模式standard和singleTop启动模式都是在原任务栈中新建Activity实例,不会启动新的Task

singleTask模式

在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。

配置形式:
<activity android:name=".SingleTaskActivity" android:launchMode="singleTask"/>
<activity android:name=".OtherTaskActivity" android:launchMode="singleTask"/>
使用案例:
public class SingleTaskActivity extends BaseActivity {
    private Button jump, jump2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_task);
        jump = (Button) findViewById(R.id.btn_task);
        jump2 = (Button) findViewById(R.id.btn_other);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTaskActivity.this, SingleTaskActivity.class);
                startActivity(intent);
            }
        });
        jump2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTaskActivity.this, OtherTaskActivity.class);
                startActivity(intent);
            }
        });
    }
}

public class OtherTaskActivity extends BaseActivity {
    private Button jump;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_task);

        jump = (Button) findViewById(R.id.btn_other);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(OtherTaskActivity.this, SingleTaskActivity.class);
                startActivity(intent);
            }
        });
    }
}
日志输出
onCreate:MainActivity TaskId: 5 hasCode:1249321980
onCreate:SingleTaskActivity TaskId: 5 hasCode:1249515136
onCreate:OtherTaskActivity TaskId: 5 hasCode:1249386172
onNewIntent:SingleTaskActivity TaskId: 5 hasCode:1249513244
测试结果:

1)从MainActiviyty进入到SingleTaskActivity,再进入到OtherActivity后,此时栈中有3个Activity实例,并且SingleTaskActivity不在栈顶。

2)而在OtherActivity跳到SingleTaskActivity时,并没有创建一个新的SingleTaskActivity,而是复用了该实例,并且回调了onNewIntent方法。并且原来的OtherActivity出栈了,具体见下面的信息,使用命令adb shell dumpsys activity activities可进行查看

Running activities (most recent first):
      TaskRecord{3c727e #11 A=com.chric.task U=0 sz=2}
        Run #1: ActivityRecord{5a00d1e u0 com.chric.task/.SingleTaskActivity t11}
        Run #0: ActivityRecord{2dce0b u0 com.chric.task/.MainActivity t11}

可以看到当前栈中只有两个Activity,即原来栈中位于SingleTaskActivity 之上的Activity都出栈了。

singleInstance-全局唯一模式

该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

singleInstance示例一配置形式:
<activity android:name=".MainActivity" android:launchMode="standard">
    <intent-filter>
       <action android:name="android.intent.action.MAIN"/>

      <category android:name="android.intent.category.LAUNCHER"/>
     </intent-filter>
</activity>
<activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance"/>
public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_standard= (Button) findViewById(R.id.btn_standard);
        btn_standard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SingleInstanceActivity.class);
                startActivity(intent);
            }
        });

    }
}
public class SingleInstanceActivity extends BaseActivity  {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_instance);
        Button button4 = (Button) findViewById(R.id.button4);
        button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleInstanceActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
    }
}
测试方式:

MainActivity--> SingleInstanceActivity--> MainActivity--> SingleInstanceActivity

日志输出

onCreate:MainActivity TaskId: 12 hasCode:201331476
onCreate:SingleInstanceActivity TaskId: 13 hasCode:57987178
onCreate:MainActivity TaskId: 12 hasCode:254253633
onNewIntent:SingleInstanceActivity TaskId: 13 hasCode:57987178

测试结果

1)两个SingleInstanceActivity是同一个实例。

2) 第一次进入的MainActivity和第一次进入的SingleInstanceActivity位于不同的task中。

3) 两个MainActivity是位于同一个task中的不同实例。

4)这个结论与预期是相同的,即,singleInstance类型的Activity的实例只能有一个,而且它只允许存在于单独的一个task中。

5)至于为什么两个MainActivity是位于同一个task中的不同实例,那是因为它是standard类型的,我们可以将ActivityTest修改为singleTop等其他类型进行测试。

singleInstance示例二配置形式:

MainActivity的模式改为"singleTop",修改后的manifest如下:

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

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

MainActivity进入SingleInstanceActivity,在从SingleInstanceActivity 进入MainActivity,在从MainActivity进入SingleInstanceActivity

日志输出

onCreate:MainActivity TaskId: 15 hasCode:201331476
onCreate:SingleInstanceActivity TaskId: 16 hasCode:57987178
onNewIntent:MainActivity TaskId: 15 hasCode:201331476
onNewIntent:SingleInstanceActivity TaskId: 16 hasCode:57987178

测试结果

1)两个SingleInstanceActivity是同一个实例。

2)第一次进入的MainActivity和第一次进入的SingleInstanceActivity位于不同的task中。

3)两个MainActivity是同一个实例。

launchMode模式总结

1. standard

在该模式下,Activity可以拥有多个实例,并且这些实例既可以位于同一个task,也可以位于不同的task。

2.singleTop

该模式下,在同一个task中,如果存在该Activity的实例,并且该Activity实例位于栈顶(即,该Activity位于前端),则调用startActivity()时,不再创建该Activity的示例;而仅仅只是调用Activity的onNewIntent()。否则的话,则新建该Activity的实例,并将其置于栈顶。

3. singleTask

只容许有一个包含该Activity实例的task存在! 总的来说:singleTask的结论与android:taskAffinity相关,以A启动B来说

1) 当A和B的taskAffinity相同时:第一次创建B的实例时,并不会启动新的task,而是直接将B添加到A所在的task;否则,将B所

在task中位于B之上的全部Activity都删除,然后跳转到B中。

2) 当A和B的taskAffinity不同时:第一次创建B的实例时,会启动新的task,然后将B添加到新建的task中;否则,将B所在task中位于B之上的全部Activity都删除,然后跳转到B中。

4. singleInstance

顾名思义,是单一实例的意思,即任意时刻只允许存在唯一的Activity实例,而且该Activity所在的task不能容纳除该Activity之外的其他Activity实例! 它与singleTask有相同之处,也有不同之处。 相同之处:任意时刻,最多只允许存在一个实例。 不同之处: 1) singleTask受android:taskAffinity属性的影响,而singleInstance不受android:taskAffinity的影响。 2) singleTask所在的task中能有其它的Activity,而singleInstance的task中不能有其他Activity。 3) 当跳转到singleTask类型的Activity,并且该Activity实例已经存在时,会删除该Activity所在task中位于该Activity之上的全部Activity实例;而跳转到singleInstance类型的Activity,并且该Activity已经存在时,不需要删除其他Activity,因为它所在的task只有该Activity唯一一个Activity实例。

实例验证singleTask启动模式与TaskAffinity属性

前面将activity的四种启动模式就基本介绍完了。为了加深对启动模式的了解,下面会通过一个简单的例子进行验证。由以上的介绍可知,standard和singleTop这两种启动模式行为比较简单,所以在下面的例子中,通过taskAffinity会对singleTask和singleInstance着重介绍。

taskAffinity

1)taskAffinity表示当前activity具有亲和力的一个任务(翻译不是很准确,原句为The task that the activity has an affinity for.),大致可以这样理解,这个 taskAffinity表示一个任务,这个任务就是当前activity所在的任务。

2)在概念上,具有相同的affinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务。

3) 一个任务的affinity决定于这个任务的根activity(root activity)的taskAffinity。

4) 默认情况下,一个应用中的所有activity具有相同的taskAffinity,即应用程序的包名。我们可以通过设置不同的taskAffinity属性给应用中的activity分组,也可以把不同的 应用中的activity的taskAffinity设置成相同的值。

5)为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task。

验证启动singleTask模式的activity时是否会创建新的任务

这个实例中有三个Activity,分别为:MainActivity,SecondActivity和ThirdActivity。

配置形式:
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name">
        <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">
        <intent-filter>
            <action android:name="com.chric.SecondActivity" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

    <activity
        android:name=".ThirdActivity"
        android:label="@string/app_name">
    </activity>
说明:

MainActivity和ThirdActivity都是标准的启动模式,而SecondActivity的启动模式为singleTask。

使用案例:
public class MainActivity extends AppCompatActivity {
    private static final String ACTIVITY_NAME = "MainActivity";
    private static final String LOG_TAG = "chric";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);

                startActivity(intent);
            }
        });

        int taskId = getTaskId();
        Log.i("chric", "onCreate:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
                startActivity(intent);
            }
        });

        Log.i("chric", "onCreate:" + getClass().getSimpleName() + " TaskId: "
                + getTaskId() + " hasCode:" + this.hashCode());

    }
}

public class ThirdActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("chric", "onCreate:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}
测试方式:

MainActivity -->SecondActivity -->ThirdActivity。

输出的日志如下:
onCreate:MainActivity TaskId: 21 hasCode:199785693
onCreate:SecondActivity TaskId: 21 hasCode:171956091
在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:
Running activities (most recent first):
   TaskRecord{838c0b8 #21 A=com.open.android.task1 U=0 sz=2}
      Run #1: ActivityRecord{abf1b0a u0 com.open.android.task1/.SecondActivity t21}
      Run #0: ActivityRecord{4e579b u0 com.open.android.task1/.MainActivity t21}
测试结果:

MainActivity和SecondActivity是启动在同一个任务中

在SecondActivity增加一个taskAffinity属性,如下所示

<activity android:name=".SecondActivity"
                  android:launchMode="singleTask"
                  android:taskAffinity="com.chric.second">
       <intent-filter >
                <action android:name="com.chric.SecondActivity"/>
                <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
</activity>
测试方式:

MainActivity -->SecondActivity -->ThirdActivity。

输出的日志如下:
onCreate:MainActivity TaskId: 24 hasCode:199785693
onCreate:SecondActivity TaskId: 25 hasCode:171956091
onCreate:ThirdActivity TaskId: 25 hasCode:76684615
在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:
Running activities (most recent first):
   TaskRecord{844539b #25 A=com.chric.second U=0 sz=2}
      Run #2: ActivityRecord{2eb5348 u0 com.open.android.task1/.ThirdActivity t25}
      Run #1: ActivityRecord{119f0df u0 com.open.android.task1/.SecondActivity t25}
   TaskRecord{b730338 #24 A=com.open.android.task1 U=0 sz=1}
      Run #0: ActivityRecord{2e05e2a u0 com.open.android.task1/.MainActivity t24}
测试结果:

1)MainActivity和SecondActivity运行在不同的任务中了

2)ThirdActivity和SecondActivity运行在同一个任务中

在这里便引出了manifest文件中的一个重要属性,taskAffinity。在官方文档中可以得到关于taskAffinity的以下信息

结果分析:

1)这就可以解释上面示例中的现象了,由第4条可知,MainActivity和SecondActivity具有不同的taskAffinity,MainActivity的taskAffinity为com.open.android.task1,SecondActivity的taskAffinity为com.chric.second。

2)当新启动的activity(SecondActivity)是以FLAG_ACTIVITY_NEW_TASK标志启动时(只有singleTask模式才会保证“single in task”,只使用FLAG_ACTIVITY_NEW_TASK并不保证“single in task”,或者说singleTask包含了FLAG_ACTIVITY_NEW_TASK,反之却不成立),framework会检索是否已经存在了一个affinity为com.chric.second的任务(即一个TaskRecord对象)

3)如果存在这样的一个任务,则检查在这个任务中是否已经有了一个SecondActivity的实例。

3-1)如果已经存在一个SecondActivity的实例,则会重用这个任务和任务中的SecondActivity实例,将这个任务调到前台,清除位于SecondActivity上面的所有Activity,显示SecondActivity,并调用SecondActivity的onNewIntent();

3-2)如果不存在一个SecondActivity的实例,会在这个任务中创建SecondActivity的实例,并调用onCreate()方法

3-3)这是在设置了singleTask模式的情况下会这样,在没有设置singleTask模式的情况下(即默认的standard模式)使用FLAG_ACTIVITY_NEW_TASK,并不会清除位于SecondActivity上面的所有Activity,而是会在task的上面重新创建一个SecondActivity。也就是说此时task中有两个SecondActivity。

4)如果不存在这样的一个任务,会创建一个新的affinity为com.chric.second的任务,并且将SecondActivity启动到这个新的任务中

上面讨论的是设置taskAffinity属性的情况,如果SecondActivity只设置启动模式为singleTask,而不设置taskAffinity,即三个Activity的taskAffinity相同,都为应用的包名,那么SecondActivity是不会开启一个新任务的,framework中的判定过程如下:

1)在MainActivity启动SecondActivity时,发现启动模式为singleTask,那么设定他的启动标志为FLAG_ACTIVITY_NEW_TASK

2)然后获得SecondActivity的taskAffinity,即为包名com.open.android.task1检查是否已经存在一个affinity为com.open.android.task1的任务,这个任务是存在的,就是MainActivity所在的任务,这个任务是在启动MainActivity时开启的

3)既然已经存在这个任务,就检索在这个任务中是否存在一个SecondActivity的实例,发现不存在在这个已有的任务中启动一个SecondActivity的实例

为了作一个清楚的比较,列出SecondActivity启动模式设为singleTask,并且taskAffinity设为com.chric.second时的启动过程

1)在MainActivity启动SecondActivity时,发现启动模式为singleTask,那么设定他的启动标志为FLAG_ACTIVITY_NEW_TASK

2)然后获得SecondActivity的taskAffinity,即com.chric.second检查是否已经存在一个affinity为com.chric.second的任务,这个任务是不存在的创建一个新的affinity为com.chric.second的任务,并且将SecondActivity启动到这个新的任务中

framework中对任务和activity的调度是很复杂的,尤其是把启动模式设为singleTask或者以FLAG_ACTIVITY_NEW_TASK标志启动时.所以,在使用singleTask和FLAG_ACTIVITY_NEW_TASK时,要仔细测试应用程序.

实例验证将两个不同app中的不同的singleTask模式的Activity的taskAffinity设成相同

官方文档中提到,可以把不同的 应用中的activity的taskAffinity设置成相同的值,这样的话这两个activity虽然不在同一应用中,却会在运行时分配到同一任务中,下面对此进行验证,在这里,会使用上面的示例,并创建一个新的示例AndroidTaskTest3。AndroidTaskTest3由两个activity组成,分别为MianActivity和OtherActivity,在MianActivity中点击按钮会启动OtherActivity,两个应用代码和布局一样,仅列出清单文件,两份清单文件如下:


    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.open.android.task1">

        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            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=".SecondActivity"
                android:launchMode="singleTask"
                android:taskAffinity="com.chric.second">
                <intent-filter>
                    <action android:name="com.chric.SecondActivity" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity android:name=".ThirdActivity" />
        </application>
    </manifest>
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.open.android.task3">

        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            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=".OtherActivity"
                android:launchMode="singleTask"
                android:taskAffinity="com.chric.second"></activity>
        </application>

    </manifest>

可以看到OtherActivity和SecondActivity的启动模式都被设置为singleTask,并且taskAffinity属性被设置为com.chric.second.现在将这两个应用安装在设备上。执行以下操作:

测试方式:

1)MainActivity --> SecondActivity

2)MainActivity --> OtherActivity

启动AndroidTaskTest应用,在它的MianActivity中点击按钮开启SecondActivity,由上面的介绍可知secondActivity是运行在一个新任务中的,这个任务就是com.chric.second。

然后按Home键回到Launcher,启动AndroidTaskTest3,在启动AndroidTaskTest3的入口MianActivity时,会自动启动新的任务,那么现在一共有三个任务,AndroidTaskTest的MianActivity和SecondActivity分别占用一个任务,AndroidTaskTest3的MianActivity也占用一个任务。

在AndroidTaskTest3的MianActivity中点击按钮启动OtherActivity,那么这个OtherActivity是在哪个任务中呢?

日志输出

***AndroidTaskTest应用测试日志输出***

onCreate:MainActivity TaskId: 29 hasCode:193251508
onCreate:SecondActivity TaskId: 30 hasCode:171956091

***AndroidTaskTest3应用测试日志输出***

onCreate:MainActivity TaskId: 31 hasCode:199785693
onCreate:OtherActivity TaskId: 30 hasCode:171956091

下面执行adb shell dumpsys activity命令,发现有以下输出:

Task id #30
  TaskRecord{7f2f34a #30 A=com.chric.second U=0 sz=2}
    Intent { flg=0x10000000 cmp=com.open.android.task1/.SecondActivity }
     Hist #1: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
       Intent { flg=0x10400000 cmp=com.open.android.task3/.OtherActivity }
       ProcessRecord{12090b5 27543:com.open.android.task3/u0a62}
      Hist #0: ActivityRecord{1048af6 u0 com.open.android.task1/.SecondActivity t30}
       Intent { flg=0x10000000 cmp=com.open.android.task1/.SecondActivity }
       ProcessRecord{5bc013e 26035:com.open.android.task1/u0a59}

 Task id #31
   TaskRecord{dce52bb #31 A=com.open.android.task3 U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task3/.MainActivity }
        Hist #0: ActivityRecord{f9e58c5 u0 com.open.android.task3/.MainActivity t31}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task3/.MainActivity }
          ProcessRecord{12090b5 27543:com.open.android.task3/u0a62}

 Task id #29
      TaskRecord{5b063d8 #29 A=com.open.android.task1 U=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task1/.MainActivity }
        Hist #0: ActivityRecord{689947d u0 com.open.android.task1/.MainActivity t29}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task1/.MainActivity }
          ProcessRecord{5bc013e 26035:com.open.android.task1/u0a59}

Running activities (most recent first):
   TaskRecord{7f2f34a #30 A=com.chric.second U=0 sz=2}
        Run #3: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
   TaskRecord{dce52bb #31 A=com.open.android.task3 U=0 sz=1}
        Run #2: ActivityRecord{f9e58c5 u0 com.open.android.task3/.MainActivity t31}
   TaskRecord{7f2f34a #30 A=com.chric.second U=0 sz=2}
       Run #1: ActivityRecord{1048af6 u0 com.open.android.task1/.SecondActivity t30}
   TaskRecord{5b063d8 #29 A=com.open.android.task1 U=0 sz=1}
       Run #0: ActivityRecord{689947d u0 com.open.android.task1/.MainActivity t29}

mResumedActivity: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
结果分析

1)所以由此可见,AndroidTaskTest1的SecondActivity和AndroidTaskTest3的OtherActivity是在同一任务中的

2)由上面adb shell dumpsys activity命令的输出结果还可以看出,AndroidTaskTest1和AndroidTaskTest3这两个应用程序会开启两个进程,他们的所有组件分别运行在独立的进程中

3)AndroidTaskTest1所在进程的进程号为u0a59,AndroidTaskTest3所在进程的进程号为u0a62

4)com.chric.second任务中的两个activity属于不同的应用,并且运行在不同的进程中,这也说明了一个问题:任务(Task)不仅可以跨应用(Application),还可以跨进程(Process)。

实例验证singleTask的另一意义:在同一个任务中具有唯一性

singleTask模式只意味着“可以在一个新的任务中开启”,至于是不是真的会在新任务中开启,在framework中还有其他条件的限制。由上面的介绍可知,这个条件为:是否已经存在了一个由他的taskAffinity属性指定的任务。这一点具有迷惑性,我们在看到singleTask这个单词的时候,会直观的想到它的本意:single in task。即,在同一个任务中,只会有一个该activity的实例。现在让我们进行验证:

为了验证这种情况,需要修改一下上面用到的AndroidTaskTest1示例。增加一个FourthActivity,并且MianActivity,SecondActivity,ThirdActivity和FourthActivity这四个activity都不设置taskAffinity属性,并且将SecondActivity启动模式设为singleTask,这样这四个activity会在同一个任务中开启。代码和软件界面就不列出了,只列出清单文件。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.open.android.task1">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".SecondActivity"
                  android:launchMode="singleTask"
                  >
         </activity>
        <activity android:name=".ThirdActivity"/>
        <activity android:name=".FourthActivity"/>
    </application>

</manifest>
测试方式:

MianActivity --> SecondActivity --> ThirdActivity --> FourthActivity

日志输出
onCreate:MainActivity TaskId: 34 hasCode:199785693
onCreate:SecondActivity TaskId: 34 hasCode:171956091
onCreate:ThirdActivity TaskId: 34 hasCode:240438941
onCreate:FourthActivity TaskId: 34 hasCode:168147209

由此可见这四个activity都是在同一个任务中的。再次执行adb shell dumpsys activity命令加以验证:

Running activities (most recent first):
  TaskRecord{d114530 #34 A=com.open.android.task1 U=0 sz=4}
     Run #3: ActivityRecord{edae6a3 u0 com.open.android.task1/.FourthActivity t34}
     Run #2: ActivityRecord{f9339a5 u0 com.open.android.task1/.ThirdActivity t34}
     Run #1: ActivityRecord{1a30096 u0 com.open.android.task1/.SecondActivity t34}
     Run #0: ActivityRecord{7e96fa4 u0 com.open.android.task1/.MainActivity t34}
测试结果:

1)同样可以说明目前这四个activity都运行在affinity为com.open.android.task1的任务中,即栈中的状态为MainActivity --> SecondActivity --> ThirdActivity --> FourthActivity。

下面执行在FourthActivity中点击按钮启动SecondActivity的操作,注意,SecondActivity的启动模式为singleTask,那么现在栈中的情况如何呢?

日志输出

没有日志输出,说明没有调用onCreate方法

再次执行adb shell dumpsys activity命令,有以下输出:

Running activities (most recent first):
  TaskRecord{d114530 #34 A=com.open.android.task1 U=0 sz=2}
     Run #1: ActivityRecord{1a30096 u0 com.open.android.task1/.SecondActivity t34}
     Run #0: ActivityRecord{7e96fa4 u0 com.open.android.task1/.MainActivity t34}
测试结果:

1)这时栈中的状态为MainActivity --> SecondActivity。确实确保了在任务中是唯一的,并且清除了同一任务中它上面的所有Activity。

2)那么这个SecondActivity的实例是重用的上次已有的实例还是重新启动了一个实例呢?可以观察系统Log, 发现系统Log没有改变,还是上面的四条Log。打印Log的语句是在各个Activity中的onCreate方法中执行的,没有打印出新的Log,说明SecondActivity的onCreate的方法没有重新执行,也就是说是重用的上次已经启动的实例,而不是销毁重建。

结果分析:

1)经过上面的验证,可以得出如下的结论:在启动一个singleTask的Activity实例时,如果系统中已经存在这样一个实例,就会将这个实例调度到任务栈的栈顶,并清除它当前所在任务中位于它上面的所有的activity。

实例验证singleInstance的行为

考谷歌官方文档,singleInstance的特点可以归结为以下三条:

1)以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例

2)以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中(官方文档上的描述为,singleInstance模式的Activity不允许其他Activity和它共存在一个任务中)

3)被singleInstance模式的Activity开启的其他activity,能够开启一个新任务,但不一定开启新的任务,也可能在已有的一个任务中开启

下面对这三个特点分别验证,所使用的示例同样为AndroidTaskTest1,只不过会进行一些修改,下面列出它的清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.open.android.task1">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".SecondActivity"
                  android:launchMode="singleInstance"
                  >
            <intent-filter>
                <action android:name="com.chric.ACTION_MY"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

        <activity android:name=".ThirdActivity"/>
        <activity android:name=".FourthActivity"/>
    </application>

</manifest>

由上面的清单文件可以知道,该应用包括四个activity,分别为MianActivity,SecondActivity,ThirdActivity,FourthActivity,其中SecondActivity启动模式设置为singleInstance。MianActivity可以开启SecondActivity,SecondActivity可以开启ThirdActivity。 并且为了可以在其他应用中开启SecondActivity,为SecondActivity设置了一个IntentFilter,这样就可以在其他应用中使用隐式Intent开启SecondActivity。

测试方式:

MianActivity --> SecondActivity --> ThirdActivity

为了更好的验证singleInstance的全局唯一性,还需要其他一个应用,新建AndroidTaskTest4。AndroidTaskTest4只需要一个MianActivity,在MainActivity中点击按钮会开启AndroidTaskTest1应用中的SecondActivity。开启AndroidTaskTest1应用中的SecondActivity的代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction("com.chric.ACTION_MY");
                startActivity(intent);
            }
        });
    }
}

下面开始验证第一个特点:以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例

执行如下操作:安装AndroidTaskTest1应用,点击MainActivity中的按钮,开启SecondActivity,可以看到如下log输出:

日志输出
onCreate:MainActivity TaskId: 35 hasCode:199785693
onCreate:SecondActivity TaskId: 36 hasCode:171956091

执行adb shell dumpsys activity命令,有以下输出:

  Running activities (most recent first):
      TaskRecord{2b68544 #36 A=com.open.android.task1 U=0 sz=1}
        Run #1: ActivityRecord{6d9f8c u0 com.open.android.task1/.SecondActivity t36}
      TaskRecord{ae9a62d #35 A=com.open.android.task1 U=0 sz=1}
        Run #0: ActivityRecord{d0429f5 u0 com.open.android.task1/.MainActivity t35}

以上可以说明,singleInstance模式的Activity总是会在新的任务中运行(前提是系统中还不存在这样的一个实例) 。

下面验证它的全局唯一性,执行以下操作:安装另一个应用AndroidTaskTest4,在开启的MainActivity中点击按钮开启AndroidTaskTest1应用中的SecondActivity。看到打印出一条新的日志:

{act=com.chric.ACTION_MY cmp=com.open.android.task1/.SecondActivity} from uid 10063 on display 0

执行adb shell dumpsys activity命令,有以下输出:

      Running activities (most recent first):
      TaskRecord{2b68544 #36 A=com.open.android.task1 U=0 sz=1}
        Run #2: ActivityRecord{6d9f8c u0 com.open.android.task1/.SecondActivity t36}
      TaskRecord{a7e9abd #37 A=com.open.android.task4 U=0 sz=1}
        Run #1: ActivityRecord{f50eec0 u0 com.open.android.task4/.MainActivity t37}
      TaskRecord{ae9a62d #35 A=com.open.android.task1 U=0 sz=1}
        Run #0: ActivityRecord{d0429f5 u0 com.open.android.task1/.MainActivity t35}
测试结果:

1)开启的SecondActivity就是上次创建的编号为6d9f8c的SecondActivity。

2)Log中没有再次输出关于SecondActivity的信息,说明SecondActivity并没有重新创建

结果分析:

以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

下面开始验证第二个特点:以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中

重新安装AndroidTaskTest1应用,点击MainActivity中的按钮,开启SecondActivity,在SecondActivity中点击按钮,开启ThirdActivity。可以看到有如下Log输出:

测试方式:

MainActivity --> SecondActivity --> ThirdActivity

日志输出:
onCreate:MainActivity TaskId: 42 hasCode:199785693
onCreate:SecondActivity TaskId: 43 hasCode:171956091
onCreate:ThirdActivity TaskId: 42 hasCode:76684615

执行adb shell dumpsys activity命令,有以下输出:

 Running activities (most recent first):
      TaskRecord{ace0710 #42 A=com.open.android.task1 U=0 sz=2}
        Run #3: ActivityRecord{322319f u0 com.open.android.task1/.ThirdActivity t42}
      TaskRecord{abc9c0e #43 A=com.open.android.task1 U=0 sz=1}
        Run #2: ActivityRecord{903a842 u0 com.open.android.task1/.SecondActivity t43}
      TaskRecord{ace0710 #42 A=com.open.android.task1 U=0 sz=2}
        Run #1: ActivityRecord{e3c0ddf u0 com.open.android.task1/.MainActivity t42}
测试结果:

SecondActivity所在的任务为43,被SecondActivity启动的ThirdActivity所在的任务为42,这就说明以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中

下面开始验证第三个特点:被singleInstance模式的Activity开启的其他activity,能够在新的任务中启动,但不一定开启新的任务,也可能在已有的一个任务中开启

有上面对第二个特点的验证可以看到,被SecondActivity启动的ThirdActivity并没有运行在一个新开启的任务中,而是和MainActivity运行在了同一个已有的任务中,那么在什么情况下ThirdActivity才会启动一个新的任务呢?

现在对程序的清单文件做以下修改,为ThirdActivity增加一个属性taskAffinity:

配置如下:
<activity android:name=".ThirdActivity"
                  android:taskAffinity="com.chirc.second"/>

重新安装AndroidTaskTest1应用,执行和上一步中同样的操作:点击MainActivity中的按钮,开启SecondActivity,在SecondActivity中点击按钮,开启ThirdActivity。可以看到有如下输出:

onCreate:MainActivity TaskId: 44 hasCode:199785693
onCreate:SecondActivity TaskId: 45 hasCode:171956091       
onCreate:ThirdActivity TaskId: 46 hasCode:76684615

执行adb shell dumpsys activity命令,有以下输出:

Running activities (most recent first):
      TaskRecord{3088b56 #46 A=com.chric.second U=0 sz=1}
        Run #2: ActivityRecord{9eff1ed u0 com.open.android.task1/.ThirdActivity t46}
      TaskRecord{f9bb7d7 #45 A=com.open.android.task1 U=0 sz=1}
        Run #1: ActivityRecord{16c7b28 u0 com.open.android.task1/.SecondActivity t45}
      TaskRecord{e9af8c4 #44 A=com.open.android.task1 U=0 sz=1}
        Run #0: ActivityRecord{5c2ed47 u0 com.open.android.task1/.MainActivity t44}
测试结果:

1)被SecondActivity启动的ThirdActivity启动在了一个新的任务中,即在启动ThirdActivity时创建了一个新任务。这就说明被singleInstance模式的Activity A在开启另一activity B时,能够开启一个新任务,但是是不是真的开启新任务,还要受其他条件的限制,这个条件是:当前系统中是不是已经有了一个activity B的taskAffinity属性指定的任务。

其实这种行为和singleTask启动时的情况相同。在Activity的启动模式设置为singleTask时,启动时系统会为它加上FLAG_ACTIVITY_NEW_TASK标志,而被singleInstance模式的Activity开启的activity,启动时系统也会为它加上FLAG_ACTIVITY_NEW_TASK标志,所以他们启动时的情况是相同的,上面再验证singleTask时已经阐述过,现在重新说明一下:

结果分析:

由于ThirdActivity是被启动模式为singleInstance类型的Activity(即SecondActivity)启动的,framework会为它它加上FLAG_ACTIVITY_NEW_TASK标志,这时 framework会检索是否已经存在了一个affinity为com.chric.second(即ThirdActivity的taskAffinity属性)的任务

1)如果存在这样的一个任务,则检查在这个任务中是否已经有了一个ThirdActivity的实例.

1-1)如果已经存在一个ThirdActivity的实例,则会重用这个任务和任务中的ThirdActivity实例,将这个任务调到前台,清除位于ThirdActivity上面的所有Activity,显示ThirdActivity,并调用ThirdActivity的onNewIntent()。

1-2)如果不存在一个ThirdActivity的实例,会在这个任务中创建ThirdActivity的实例,并调用onCreate()方法

1-3)需要注意(1-1)有一种特殊情况,MainActivity, SecondActivity, ThirdActivity, FourthActivity 都不设置 taskAffinity.FourthActivity 设置为 singleInstance。测试 MainActivity -> SecondActivity -> ThirdActivity -> FourthActivity -> SecondActivity,从 FourthActivity 跳转到 SecondActivity, 是新开的一个 SecondActivity, 不会销毁 原 SecondActivity 上面的 ThirdActivity。

2)如果不存在这样的一个任务,会创建一个新的affinity为com.chric.second的任务,并且将ThirdActivity启动到这个新的任务中.

如果ThirdActivity不设置taskAffinity,即ThirdActivity和MainActivity的taskAffinity相同,都为应用的包名,那么ThirdActivity是不会开启一个新任务的.

framework中的判定过程如下:

1)在SecondActivity启动ThirdActivity时,因为SecondActivity是singleInstance的,所以设定ThirdActivity的启动标志为FLAG_ACTIVITY_NEW_TASK

2)然后获得ThirdActivity的taskAffinity,即为包名com.open.android.task1

3)检查是否已经存在一个affinity为com.open.android.task1的任务,这个任务是存在的,就是MainActivity所在的任务,这个任务是在启动MainActivity时开启的

4) 既然已经存在这个任务,就检索在这个任务中是否存在一个ThirdActivity的实例,发现不存在

5)在这个已有的任务中启动一个SecondActivity的实例

为了作一个清楚的比较,列出ThirdActivity的taskAffinity属性设为com.chric.second时的启动过程

1)在SecondActivity启动ThirdActivity时,因为SecondActivity是singleInstance的,那么设定ThirdActivity的启动标志为FLAG_ACTIVITY_NEW_TASK

2)然后获得ThirdActivity的taskAffinity,即为com.chric.second

3)检查是否已经存在一个affinity为com.chric.second的任务,这个任务是不存在的

4) 创建一个新的affinity为com.chric.second的任务,并且将ThirdActivity启动到这个新的任务

到此singleInstance也介绍完了。

小结

由上述可知,Task是Android Framework中的一个概念,Task是由一系列相关的Activity组成的,是一组相关Activity的集合。Task是以栈的形式来管理的。

我们在操作软件的过程中,一定会涉及界面的跳转。其实在对界面进行跳转时,Android Framework既能在同一个任务中对Activity进行调度,也能以Task为单位进行整体调度。在启动模式为standard或singleTop时,一般是在同一个任务中对Activity进行调度,而在启动模式为singleTask或singleInstance是,一般会对Task进行整体调度。

对Task进行整体调度包括以下操作:

1)按Home键,将之前的任务切换到后台 2)长按Home键,会显示出最近执行过的任务列表 3)在Launcher或HomeScreen点击app图标,开启一个新任务,或者是将已有的任务调度到前台 4)启动singleTask模式的Activity时,会在系统中搜寻是否已经存在一个合适的任务,若存在,则会将这个任务调度到前台以重用这个任务。如果这个任务中已经存在一个要启动的Activity的实例,则清除这个实例之上的所有Activity,将这个实例显示给用户。如果这个已存在的任务中不存在一个要启动的Activity的实例,则在这个任务的顶端启动一个实例。若这个任务不存在,则会启动一个新的任务,在这个新的任务中启动这个singleTask模式的Activity的一个实例。 5)启动singleInstance的Activity时,会在系统中搜寻是否已经存在一个这个Activity的实例,如果存在,会将这个实例所在的任务调度到前台,重用这个Activity的实例(该任务中只有这一个Activity),如果不存在,会开启一个新任务,并在这个新任务中启动这个singleInstance模式的Activity的一个实例。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值