在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存顺序,那么,在新启动一个activity时,栈的里面是每次都新增实例还是只有一个实例呢?这个与在清单文件注册一个activity使用的参数:launchMode有关。

  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掉。

Android开发之---Activity启动模式的更多相关文章

  1. android:process结合activity启动模式的一次实践

    会有这样的场景,一个应用崩溃了,而导致的该应用崩溃的原因是,该应用占用的内存大小超过了系统分配给它的最大堆大小.对象的分配,是发生在堆(heap)上面的,系统分配给每个应用的最大堆大小是固定的. 假设 ...

  2. Android开发9——Activity的启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. 一. ...

  3. Android中Activity启动模式详解

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  4. Android Activity 启动模式详解

    最近有群里的朋友问我 Activity的四种启动模式分别是什么意思? 当初因为项目比较忙,草草的解释了下, Api文档中说的也只是一般,在这里就小记一下吧,以便有更多的朋友对Activity启动模式了 ...

  5. 【转】Android总结篇系列:Activity启动模式(lauchMode)

    [转]Android总结篇系列:Activity启动模式(lauchMode) 本来想针对Activity中的启动模式写篇文章的,后来网上发现有人已经总结的相当好了,在此直接引用过来,并加上自己的一些 ...

  6. AndroidのActivity启动模式

    Activity启动模式      .概念      Activity启动模式定义了Activity启动的规则,它决定着Activity的实例创建与重用与否    .属性     Activity的启 ...

  7. Android组件体系之Activity启动模式解析

    本文主要分析Activity的启动模式及使用场景. 一.Activity启动模式浅析 1.standard 标准模式,系统默认的启动模式.在启动Activity时,系统总是创建一个新的Activity ...

  8. Activity启动模式的深入分析

    网上关于Activity启动模式的文章许多.可是看起来都千篇一律,看完之后我们都能理解这4种启动模式.只是官方api对singleTask这个启动模式解释有些争议,导致我事实上并没有真正理解这几种模式 ...

  9. Activity启动模式 及 Intent Flags 与 栈 的关联分析

     http://blog.csdn.net/vipzjyno1/article/details/25463457    Android启动模式Flags栈Task   目录(?)[+] 什么是栈 栈 ...

随机推荐

  1. WPF资源字典的使用【转】

    资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情. 1.  创建资源字典 创建资源字典的过程比较简单,只 ...

  2. IE下get传中文乱码的问题完美解决方案

    前几天做项目的时候遇到需要在easyui的combobox的url中以get的方式传中文,出现乱码. $('#cc').combobox({ url : 'xxxAction.action?para= ...

  3. php 如何解决“您访问的域名有误或网页不存在”

    对一个域名访问,在家访问正常,到办公室就总是看到下面的页面. 清楚浏览器浏览数据是一个解决途径.这里通过Chrome浏览做示例.通过标记路径,进入清除界面. 也可以在浏览器地址中输入 chrome:/ ...

  4. transition

    transition: all 所有属性都将获得过渡效果. property 定义应用过渡效果的 CSS 属性名称列表,列表以逗号分隔 transition-duration 规定完成过渡效果需要多少 ...

  5. 【Codeforces715C&716E】Digit Tree 数学 + 点分治

    C. Digit Tree time limit per test:3 seconds memory limit per test:256 megabytes input:standard input ...

  6. POJ2763 Housewife Wind

      Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 9701   Accepted: 2661 Description Aft ...

  7. 【Beta】Scrum05

    Info 由于12.9~12.11三天,PM和测试到上海参加比赛,期间PM对博客更新暂停,功能测试暂停,Scrum会议暂停,12.12日起补充及恢复之前未能完成的工作. 时间:2016.12.08 2 ...

  8. mysql关联表的复制

    1. 复制被参照的表: CREATE TABLE clone_product_1 LIKE product_1; INSERT INTO clone_product_1 SELECT * FROM p ...

  9. ThinkPHP 事务处理 (事务回滚) 、异常处理

    $tran_result = true;                $trans = M();                $trans->startTrans(); try {      ...

  10. oracle数据库_实例_用户_表空间之间的关系(转)

    数据库:Oracle数据库是数据的物理存储.这就包括(数据文件ORA或者DBF.控制文件.联机日志.参数文件).其实Oracle数据库的概念和其它数据库不一样,这里的数据库是一个操作系统只有一个库.可 ...