Acvitity启动模式有4种:
1. 标准模式standard
2. 栈顶复用模式singleTop
3. 栈内复用模式singleTask
4. 单实例模式singleInstance
为了测试这几种模式,新建了一个测试工程:https://github.com/linjk/TestLaunchMode,可以下载代码进行测试。
1. 新建测试项目:
2. 新建一个类,用于管理activity栈和打印当前栈的activity名字,代码如下:
package cn.linjk.testlaunchmode.utils;
import android.app.Activity;
import android.util.Log;
import java.util.Stack;
/**
* Created by LinJK on 05/12/2016.
*
* <p>Class to manage activity stack, base activity
* can use it.</p>
*
* @version 1.0
*/
public class ActivityStackManager {
/**
* Single Instance
* @return
*/
public static ActivityStackManager getINSTANCE() {
if (null == INSTANCE) {
INSTANCE = new ActivityStackManager();
}
return INSTANCE;
}
//region Add and remove activity
public void addActivity(Activity ac) {
if (null == stack_activity) {
stack_activity = new Stack<>();
}
stack_activity.add(ac);
printAllActivityName();
}
public void removeActivity(Activity ac) {
if (ac != null) {
stack_activity.remove(ac);
}
printAllActivityName();
}
//endregion
//region Print Activity Stack Info
public void printAllActivityName() {
for (int i = stack_activity.size()-1; i >= 0; i--) {
Log.d(TAG, "[" + i + "]Activity Name: " + stack_activity.get(i).getClass().getName());
}
}
//endregion
///
private ActivityStackManager() {}
private static final String TAG = ActivityStackManager.class.getSimpleName();
private static ActivityStackManager INSTANCE;
private static Stack<Activity> stack_activity;
}
3. MainActivity作为第一个页面,这里再新建第二个activity:
两个页面的布局xml文件分别如下:
MainActivity:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="cn.linjk.testlaunchmode.activity.MainActivity">
<Button
android:id="@+id/btn_start_new_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动新的activity_Main"/>
</RelativeLayout>
ActivitySecond:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_start_new_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动新的activity_Second"/>
</LinearLayout>
MainActivity.java:
package cn.linjk.testlaunchmode.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import cn.linjk.testlaunchmode.base.activity.BaseActivity;
import cn.linjk.testlaunchmode.R;
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);
btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
startActivity(new Intent(MainActivity.this, ActivitySecond.class));
}
});
}
//
private Button btnStartNewActivity;
}
ActivitySecond.java:
package cn.linjk.testlaunchmode.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import cn.linjk.testlaunchmode.R;
import cn.linjk.testlaunchmode.base.activity.BaseActivity;
/**
* Created by LinJK on 05/12/2016.
*/
public class ActivitySecond extends BaseActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);
btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
startActivity(new Intent(ActivitySecond.this, ActivitySecond.class));
}
});
}
//
private Button btnStartNewActivity;
}
在清单文件中的application节点,activity配置如下,都是默认的启动模式配置:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".activity.ActivitySecond"/>
</application>
4. 启动模式测试:
APP在启动后,未配置情况下,默认会分配一个任务栈,所有的actiivty默认都会在该栈启动与退出,这是通过任务共用性(Affinities)完成的,任务共用性
为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字,可以在acitivty的onCreate启动后通过getTaskID获取其所在任务栈id,新启动一个页面后,新的页面会在启动它的任务栈打开,如果加了标志:FLAG_ACTIVITY_NEW_TASK,那个这两个activity就会在两个不同的任务栈里面。
注意,只有actvitivy的上下文才可以启动activity,因为这才存在任务栈,而application级别的,如getApplicationContext().startActivity(new Intent(MainActivity.this, ActivitySecond.class));则无法启动,因为application的上下文并不会分配上下文,需要加新增任务栈标记位:FLAG_ACTIVITY_NEW_TASK。
4.1 standard
从第一个页面进入第二个页面后,在第二个页面再启动第二个页面,打印的日志如下:
可以看到,默认的standard模式,每次都会新建实例来启动activity.
4.2 singleTop
配置第二个页面的启动模式:
<activity
android:launchMode="singleTop"
android:name=".activity.ActivitySecond"/>
第一次进入第二个页面日志如下:
再点击启动新页面:
这是发现只打印多红色圈住的这一句日志,没调用onCreate() ?
查看一下activity源码,发现这部分:
原来,配置了singleTop模式后,会调用onNewIntent()重新加载页面,我们修改ActivitySecond代码,增加如下部分:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("ActivitySecond", "[Task ID]" + getTaskId());
ActivityStackManager.getINSTANCE().printAllActivityName();
}
重复刚才操作,日志如下:
可以发现,当栈顶是ActivitySecond,重复打开时只有一个ActivitySecond实例。注意,如果ActivitySecond不是在栈顶,新打开还是会新建的!
4.3 singleTask
与singleTop类似,就不重复测试了,大概说明一下差异:
1. singleTop是在栈顶在复用,不在栈顶则新建,而singleTask是只要在任务栈内就复用,不会新建
2. singleTask模式中,默认具有clearTop效果,如果当前栈状态ABCD, 此时新建B,会把B页面移到栈顶,并清除原来B以上的栈页面,此时变为AB。
3. 使用singleTask,可以在清单文件加上taskAffinity属性,其值不能与包名一致,否则会无效,这样,后面以singleTask启动的activity,如果该属性一样,就会分配到同一个新的任务栈中。
(
附:如一个activity的taskAffnity与allowTasReparenting属性配合使用,那么该activity可以在两个app的任务栈共用,假设该activity叫C,现有两个应用A、B,名叫activity的C注册在B应用的清单文件中,配置如下:
<activity
android:name=".activity.ActivitySecond">
<intent-filter>
<action android:name="android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
则应用A中启动该activity可如下启动:
Intent intent = new Intent("android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY");
startActivity(intent);
此时,如果应用B打开C,会把C从应用A的任务栈中转移过来。
)
4.4 singleInstance
singleTask的加强版本,以此模式启动的activtiy,系统会新建一个任务栈并把它独立存放,这个activity后面会被重复使用,直到系统把该任务栈kill掉。
注意:
如果在一个singleTop或singleInstance的ActivityA中通过startActivityForResult()方法来启动另一个ActivityB,那么系统将直接返回Activity.RESLUT_CANCELED而不会去等待返回,因为不同task之间,默认是不能传递数据的,如要传递也需要通过Intent来绑定数据。
5. 通过Intent Flag启动
可以在启动activity的intenr来设置flag来启动一个新的activity,常用的Flag如下:
Intent.FLAG_ACTIVITY_NEW_TASK: 常见于在service中启动activity,因为service不在activity的栈中,此种方式是创建一个新的activity栈
Intent.FLAG_ACTIVIVY_SINGLE_TOP: 等同于singleTop
Intent.FLAG_ACTIVITY_CLEAR_TOP: 等同于singleTask
Intent.FLAG_ACTIVITY_NO_HISTORY: A启动B后,A就会被销毁,任务栈中只存在B