原地址:https://blog..net/mynameishuangshuai/article/details/51491074

最近有几位朋友给我留言,让我谈一下对Activity启动模式的理解。我觉得对某个知识点的理解必须要动手操作才能印象深刻,所以今天写一篇博文,结合案例理解Activity启动模式。由于之前看过“区长”的一篇博文(文章结尾处有链接)深受启发,因此本文是在那篇文章的基础上更加全面的讲解。

众所周知当我们多次启动同一个Activity时,系统会创建多个实例,并把它们按照先进后出的原则一一放入任务栈中,当我们按back键时,就会有一个activity从任务栈顶移除,重复下去,直到任务栈为空,系统就会回收这个任务栈。但是这样以来,系统多次启动同一个Activity时就会重复创建多个实例,这种做法显然不合理,为了能够优化这个问题,Android提供四种启动模式来修改系统这一默认行为。

进入正题,Activity的四种启动模式如下:

standard、singleTop、singleTask、singleInstance

接下来,我们一边讲理论一边结合案例来全面学习这四种启动模式。

为了打印方便,定义一个基础Activity,在其onCreate方法和onNewIntent方法中打印出当前Activity的日志信息,主要包括所属的task,当前类的hashcode,以及taskAffinity的值。之后我们进行测试的Activity都直接继承该Activity

import android.content.Intent;

import android.content.pm.ActivityInfo;

import android.content.pm.PackageManager;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.util.Log;

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

* 方便打印的基础Activity

*/

public class BaseActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Log.i("WooYun", "*****onCreate()方法******");

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

dumpTaskAffinity();

}

@Override

protected void onNewIntent(Intent intent) {

super.onNewIntent(intent);

Log.i("WooYun", "*****onNewIntent()方法*****");

Log.i("WooYun", "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("WooYun", "taskAffinity:"+info.taskAffinity);

} catch (PackageManager.NameNotFoundException e) {

e.printStackTrace();

}

}

}

standard-默认模式

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

配置形式:

使用案例:

对于standard模式,android:launchMode可以不进行声明,因为默认就是standard。

StandardActivity 的代码如下,入口Activity中有一个按钮进入该Activity,这个Activity中又有一个按钮启动StandardActivity。

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

* Standard模式

*/

public class ActivityStandard extends BaseActivity {

private Buttonjump;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_standard);

jump= (Button) findViewById(R.id.btn_standard);

jump.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(ActivityStandard.this, ActivityStandard.class);

startActivity(intent);

}

});

}

}

我们首先进入StandardActivity,进入后再点击进入Standard的按钮,再按四次返回键不断返回。

输出的日志如下:

可以看到日志输出了四次StandardActivity的和一次MainActivity的,从MainActivity进入StandardActivity一次,后来我们又按了三次按钮,总共四次StandardActivity的日志,并且所属的任务栈的id都是2087,这也验证了谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中这句话,因为启动StandardActivity的是MainActivity,而MainActivity的taskId是2087,因此启动的StandardActivity也应该属于id为2087的这个task,后续的3个StandardActivity是被StandardActivity这个对象启动的,因此也应该还是2087,所以taskId都是2087。并且每一个Activity的hashcode都是不一样的,说明他们是不同的实例,即“每次启动一个Activity都会重写创建一个新的实例”

singleTop-栈顶复用模式

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

配置形式:

使用案例:

ActivitySingleTop.java

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

* SingleTop模式

*/

public class ActivitySingleTop 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.btn_singletop);

jump2 = (Button) findViewById(R.id.btn_other);

jump.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(ActivitySingleTop.this, ActivitySingleTop.class);

startActivity(intent);

}

});

jump2.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(ActivitySingleTop.this, OtherTopActivity.class);

startActivity(intent);

}

});

}

OtherTopActivity.java

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

*/

public class OtherTopActivity extends BaseActivity {

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, ActivitySingleTop.class);

startActivity(intent);

}

});

}

}

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

我们看到,除了第一次进入SingleTopActivity这个Activity时,输出的是onCreate方法中的日志,后续的都是调用了onNewIntent方法,并没有调用onCreate方法,并且四个日志的hashcode都是一样的,说明栈中只有一个实例。这是因为第一次进入的时候,栈中没有该实例,则创建,后续的三次发现栈顶有这个实例,则直接复用,并且调用onNewIntent方法。那么假设栈中有该实例,但是该实例不在栈顶情况又如何呢?

我们先从MainActivity中进入到SingleTopActivity,然后再跳转到OtherActivity中,再从OtherActivity中跳回SingleTopActivity,再从SingleTopActivity跳到SingleTopActivity中,看看整个过程的日志。

我们看到从MainActivity进入到SingleTopActivity时,新建了一个SingleTopActivity对象,并且task id与MainActivity是一样的,然后从SingleTopActivity跳到OtherActivity时,新建了一个OtherActivity,此时task中存在三个Activity,从栈底到栈顶依次是MainActivity,SingleTopActivity,OtherActivity,此时如果再跳到SingleTopActivity,即使栈中已经有SingleTopActivity实例了,但是依然会创建一个新的SingleTopActivity实例,这一点从上面的日志的hashCode可以看出,此时栈顶是SingleTopActivity,如果再跳到SingleTopActivity,就会复用栈顶的SingleTopActivity,即会调用SingleTopActivity的onNewIntent方法。这就是上述日志的全过程。

对以上内容进行总结

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

singleTop模式分3种情况

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

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

当前栈中不存在该Activity的实例时,其行为同standard启动模式

standard和singleTop启动模式都是在原任务栈中新建Activity实例,不会启动新的Task,即使你指定了taskAffinity属性。

那么什么是taskAffinity属性呢,可以简单的理解为任务相关性。

这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名

我们可以单独指定每一个Activity的taskAffinity属性覆盖默认值

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

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

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

很重要的一点taskAffinity属性不对standard和singleTop模式有任何影响,即时你指定了该属性为其他不同的值,这两种启动模式下不会创建新的task(如果不指定即默认值,即包名)

指定方式如下:

singleTask-栈内复用模式

这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。

配置形式:

使用案例:

ActivitySingleTask.java

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

* SingleTask模式

*/

public class ActivitySingleTask 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(ActivitySingleTask.this, ActivitySingleTask.class);

startActivity(intent);

}

});

jump2.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(ActivitySingleTask.this, OtherTaskActivity.class);

startActivity(intent);

}

});

}

}

OtherTaskActivity.java

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

/**

* Created by huangshuai on 2016/5/23.

* Email:huangshuai@wooyun.org

*/

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, ActivitySingleTask.class);

startActivity(intent);

}

});

}

}

现在我们先不指定任何taskAffinity属性,对它做类似singleTop的操作,即从入口MainActivity进入SingleTaskActivity,然后跳到OtherActivity,再跳回到SingleTaskActivity。看看整个过程的日志。

当我们从MainActiviyty进入到SingleTaskActivity,再进入到OtherActivity后,此时栈中有3个Activity实例,并且SingleTaskActivity不在栈顶,而在OtherActivity跳到SingleTaskActivity时,并没有创建一个新的SingleTaskActivity,而是复用了该实例,并且回调了onNewIntent方法。并且原来的OtherActivity出栈了,具体见下面的信息,使用命令adb shell dumpsys activity activities可进行查看

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

我们看到使用singleTask启动模式启动一个Activity,它还是在原来的task中启动。其实是这样的,我们并没有指定taskAffinity属性,这说明和默认值一样,也就是包名,当MainActivity启动时创建的Task的名字就是包名,因为MainActivity也没有指定taskAffinity,而当我们启动SingleTaskActivity ,首先会寻找需要的任务栈是否存在,也就是taskAffinity指定的值,这里就是包名,发现存在,就不再创建新的task,而是直接使用。当该task中存在该Activity实例时就会复用该实例,这就是栈内复用模式。

这时候,如果我们指定SingleTaskActivity 的taskAffinity值。

还是之前的操作。但是日志就会变得不一样。

我们看到SingleTaskActivity所属的任务栈的TaskId发生了变换,也就是说开启了一个新的Task,并且之后的OtherActivity也运行在了该Task上

打印出信息也证明了存在两个不同的Task

如果我们指定MainActivity的taskAffinity属性和SingleTaskActivity一样,又会出现什么情况呢。

没错,就是和他们什么都不指定是一样的。

这时候,就有了下面的结论

singleTask启动模式启动Activity时,首先会根据taskAffinity去寻找当前是否存在一个对应名字的任务栈

如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去

如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例

如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法

如果不存在该实例,则新建Activity,并入栈

此外,我们可以将两个不同App中的Activity设置为相同的taskAffinity,这样虽然在不同的应用中,但是Activity会被分配到同一个Task中去。

我们再创建另外一个应用,指定它的taskAffinity和之前的一样,都是com.xingyu.demo.singletask

然后启动一个应用,让他跳转到该Activity后,再按home键后台,启动另一个应用再进入该Activity,看日志

我们看到,指定了相同的taskAffinity的SingleTaskActivity和OtherActivity被启动到了同一个task中,taskId都为2169。

singleInstance-全局唯一模式

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

配置形式:

使用案例:

增加一个Activity如下:

ActivitySingleInstance.java

import android.os.Bundle;

/**

* Created by huangshuai on 2016/5/24.

* Email:huangshuai@wooyun.org

* SingleInstance模式

*/

public class ActivitySingleInstance extends BaseActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_singleinstance);

}

}

配置属性如下:

android:name=".ActivitySingleInstance"

android:launchMode="singleInstance">

使用下面的方式分别在两个应用中启动它

Intent intent = new Intent();

intent.setAction("com.castiel.demo.singleinstance");

startActivity(intent);

做的操作和上一次是一样的,查看日志

我们看到,第一个应用启动SingleInstanceActivity时,由于系统中不存在该实例,所以新建了一个Task,按home键后,使用另一个App进入该Activity,由于系统中已经存在了一个实例,不会再创建新的Task,直接复用该实例,并且回调onNewIntent方法。可以从他们的hashcode中可以看出这是同一个实例。因此我们可以理解为:SingleInstance模式启动的Activity在系统中具有全局唯一性。

参考链接:http://blog..net/sbsujjbcy/article/details/49360615

android的四种启动模式,(转)彻底弄懂Activity四大启动模式相关推荐

  1. Android:四种启动模式分析

    2019独角兽企业重金招聘Python工程师标准>>> 在一个项目中会包括着多个Activity,系统中使用任务栈来存储创建的Activity实例,任务栈是一种"后进先出& ...

  2. Android中四种补间动画的使用示例(附代码下载)

    场景 Android中四种补间动画. 透明度渐变动画 旋转动画 缩放动画 平移动画 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的 ...

  3. Android中四种启动模式,最容易理解的小白教程

    说起Android中的启动模式,其实是个很基础的内容,谁都知道是4中启动模式,没错分别是以下四种: 1,standard 2,singleTop 3,singleTask 4,singleInstan ...

  4. android sharedpre,Android SharedPreferences四种操作模式使用详解_Android_脚本之家

    Android  SharedPreferences详解 获取SharedPreferences的两种方式: 1 调用Context对象的getSharedPreferences()方法 2 调用Ac ...

  5. android软引用如何使用方法,Android的四种引用(强引用、弱引用、软引用、虚引用)...

    前言:满纸荒唐言,一把辛酸泪:都云作者痴,谁解其中味. 一.概述 早在JDK1.2就把对象引用分为四种级别,从而使程序能更灵活控制它的生命周期,级别由高到底依次为:强 > 软 > 弱 &g ...

  6. htc one android版本,四种版本三种双卡 行货版HTC One发布

    四种版本三种双卡 行货版HTC One真机试玩 和国际版HTC One不同的是,中国大陆的用户会意外的发现,行货版的HTC One不仅支持中国的三大3G网络,而且均增加了双卡双待的功能,当然还有一个支 ...

  7. Android筑基——Activity的启动过程之同进程在一个Activity中启动另一个Activity(基于api21)

    目录 1. 前言 2. 正文 2.1 Activity类的startActivity()方法 2.2 Instrumentation类的execStartActivity()方法 2.3 Activi ...

  8. 数商云:弄懂农贸B2B发展模式的核心逻辑,让农业生意更好做

    中国人追崇"民以食为天".据国家统计局发布数据显示,2018年全国餐饮收入实现42716亿元,比上年增长9.5%,首次超过4万亿元大关.再次验证了这项古老的"中国传统文化 ...

  9. android的四种对象引用级别:强、弱、软、虚引用

    android中的强引用,弱引用,软引用,虚引用.代表了android中的对象引用级别,也即代表了java的垃圾回收机制,看可以简单理解为内存释放. 1.强引用:强引用是使用最普遍的引用.如果一个对象 ...

最新文章

  1. 关于玻璃体手术的最终建议
  2. iOS开发篇——赋值语句介绍(C++)
  3. java模拟时间_使用生成器模拟时间分片
  4. java ssm框架调用微信,微信小程序实现前后台交互(后台使用ssm框架)
  5. 用stack实现括号匹配
  6. 怎样对流媒体进行压力测试_loadrunner流媒体压力测试
  7. 声艺数字调音台si说明书32路_声艺Si Expression 3 32路数字调音台
  8. 毕业设计 STM32单片机的空气质量检测系统
  9. android packageManager用法
  10. 你画过哪些好玩儿的作品?
  11. 留学目的地选择之伊利诺伊州
  12. Discuz X 多页面专题制作教程
  13. 【语义分割】2021-PVT2 CVMJ
  14. 跳过wifi认证直接上网
  15. 微信小程序 表情小作坊 — 轻松定制表情包
  16. 34万奖金!第三届厦门国际银行数创金融杯金融营销大赛来啦!
  17. 大数据工程师修炼笔记
  18. ARP渗透与攻防(五)之Ettercap劫持用户流量
  19. 阿里云企业发展史中的重大事件一览
  20. 【后端检测-绕过】文件头检测绕过、二次渲染绕过

热门文章

  1. SAP云平台上的502 Bad Gateway错误
  2. Cloud for Customer UI checkbox控件在PC和mobile端的不同显示
  3. Use BAdI - call_workitem_exit workflow
  4. Java三维数组的使用
  5. JavaScript事件处理的例子:事件捕捉和冒泡 - event capture and bubble
  6. HTML CSS里display:block的原理和用法
  7. SAP UI5的source code map(源代码映射)机制
  8. SAP云平台CloudFoundry编程环境下app router的使用最佳实践
  9. SAP CRM configuration product在UI上的显示逻辑
  10. SAP CRM里是如何检测一个BP是否存在的 - FM BP_CENTRALPERSON_GET