在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. golang使用yaml格式解析构建配置文件

    现在主流的配置文件格式有这么几种,xml.yaml.config…  xml就算了,太挫了,太土, 太繁琐… config 就是mysql,apache my.cnf的那种格式,这个格式适合功能分层, ...

  2. Mysql索引PRIMARY、NORMAL、UNIQUE、FULLTEXT 区别和使用场合

    索引 数据库的索引就像一本书的目录,能够加快数据库的查询速度. MYSQL索引有四种PRIMARY.INDEX.UNIQUE.FULLTEXT, 其中PRIMARY.INDEX.UNIQUE是一类,F ...

  3. yii2.0归档安装方法

    我前几天用composer安装 一直没成功  我就用归档的方法安装了  所以这篇文字只帮助那些用归档方法安装的朋友 Yii是一个高性能的,适用于开发WEB2.0应用的PHP框架. Yii自带了丰富的功 ...

  4. Alpha事后诸葛亮

    Aruba小组Cento项目Postmortem 队员: 408 409 410 428 429 431   设想和目标 1.我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰 ...

  5. bae上传图片失败

    出错原因:bae不支持文件写入.

  6. 关于 DataGridTextColumn的IsReadOnly

    1. 以下是绑定方式,但是IsReadOnly不起作用 <DataGrid x:Name="dgTest" ItemsSource="{Binding}" ...

  7. <<< html5本地储存

    类似与Cookies,但由于Cookies储存量太小,大小也只有4-5KB的样子,html5的本地储存能存5M大小的数据 html5本地储存属性有,localStorage和sessionStorag ...

  8. 使用css打造形形色色的形状!

    使用css打造形形色色的形状! css是非常强大的工具,如果我们掌握的好,那么许多复杂的形状不需要使用图片而直接使用css完成即可,这不仅有利于减少http请求以增强性能还便于日后的管理和维护,一举两 ...

  9. webpack解惑:require的五种用法 (转)

    我之前在 <前端搭环境之从入门到放弃>这篇文章中吐槽过,webpack中可以写commonjs格式的require同步语法,可以写AMD格式的require回调语法,还有一个require ...

  10. Solr学习总结(七)Solr搜索引擎的整体架构

    经过前面一段时间的努力,终于把我所知道的关于solr 的内容都总结完了.前面讲到了solr 的安装配置,web管理后台的使用,solr 的查询参数和查询语法,还说到了solr的客户端 solrnet  ...