关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android Activity 启动模式的功能验证

    Android Activity 启动模式的功能验证

    2015-10-24 00:00:00 出处:阳春面
    分享

    之前一直都是看别人写的启动模式,发现网上大多数的内容都是抄袭来抄袭去,直到最近看了开发艺术这本书,发现之前对启动模式的理解过于简单,很多东西都没有考虑到,为了加深理解,于是决定自己动手去验证一下四个启动模式。当然我们也从最简单的启动模式开始验证。

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

    public class BaseActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.e("TAG", "===========================================onCreate=========================================================");
            Log.e("TAG", "onCreate " + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
            dumpTaskAffinity();
        }
    
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            Log.e("TAG", "===========================================onNewIntent=========================================================");
            Log.e("TAG", "onNewIntent " + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
            dumpTaskAffinity();
        }
    
        protected void dumpTaskAffinity(){
            try {
                ActivityInfo info = this.getPackageManager()
                        .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
                Log.e("TAG", "taskAffinity:"+info.taskAffinity);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    standard模式

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

    新建一个Activity,并声明在manifest文件中

    <activity android:name=".standard.StandardActivity"
              android:launchMode="standard"
        >
    </activity>

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

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

    public class StandardActivity extends BaseActivity {
        private Button jump;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_standard);
    
            jump= (Button) findViewById(R.id.jump);
            jump.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(StandardActivity.this, StandardActivity.class);
                    startActivity(intent);
                }
            });
        }
    
    }

    看下gif动态图,有点卡顿,凑合看,我们首先进入StandardActivity,进入后再点击**测试Standard**的按钮,再按四次返回键不断返回。

    输出的日志如下

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

    singleTop,栈顶复用模式

    这个模式下,假如新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用。假如栈顶不存在该Activity的实例,则情况与standard模式相同。

    SingleTopActivity代码和StandardActivity类似,只不过记得在manifest文本后中修改启动模式。

    <activity android:name=".singletop.SingleTopActivity"
                  android:launchMode="singleTop">

    操作和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

    我们指定之前的例子的taskAffinity分别为其他不同的值,入口Activity不指定(不指定即默认值,即包名)

    <activity android:name=".standard.StandardActivity"
              android:launchMode="standard"
              android:taskAffinity="cn.edu.zafu.lifecycle.standard"
        >
    </activity>
    
    <activity android:name=".singletop.SingleTopActivity"
              android:launchMode="singleTop"
              android:taskAffinity="cn.edu.zafu.lifecycle.singletop"
        >
    </activity>

    分别启动这两个Activity看日志输出

    这里写图片描述

    我们看到入口Activity的taskAffinity值就是包名,就是默认情况下不指定的。然后StandardActivity和SingleTopActivity的taskAffinity值被我们覆盖了,分别为不同的值,但是这两个Activity启动的时候任务栈并没有新建,而是直接在原来的Task中启动,这说明这个taskAffinity对这两种启动模式没有什么影响。其实该属性主要是配合SingleTask启动模式使用的。接下来我们看该启动模式,可以说这个启动模式是最复杂的。

    singleTask,即栈内复用模式

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

    指定Activity为singleTask模式

    <activity android:name=".singleTask.SingleTaskActivity"
              android:launchMode="singleTask"
        >
    </activity>

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

    这里写图片描述

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

    Running activities (most recent first):
    TaskRecord{434c6d90 #77 A=cn.edu.zafu.lifecycle U=0 sz=2}
    Run #2: ActivityRecord{4358c850 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t77}
    Run #1: ActivityRecord{43576720 u0 cn.edu.zafu.lifecycle/.MainActivity t77}

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

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

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

    <activity android:name=".singleTask.SingleTaskActivity"
              android:launchMode="singleTask"
              android:taskAffinity="cn.edu.zafu.lifecycle.singleTask"
        >
    </activity>

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

    这里写图片描述

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

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

    Running activities (most recent first):
    TaskRecord{441ab678 #79 A=cn.edu.zafu.lifecycle.singleTask U=0 sz=1}
    Run #3: ActivityRecord{432fa178 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t79}
    TaskRecord{43ccd2a8 #78 A=cn.edu.zafu.lifecycle U=0 sz=1}
    Run #2: ActivityRecord{42d5e7a8 u0 cn.edu.zafu.lifecycle/.MainActivity t78}

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

    这里写图片描述

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

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

    singleTask启动模式启动Activity时,首先会根据taskAffinity去寻找当前是否存在一个对应名字的任务栈 假如不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去 假如存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例 假如存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法 假如不存在该实例,则新建Activity,并入栈

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

    我们再创建另外一个应用,指定它的taskAffinity和之前的一样,都是cn.edu.zafu.lifecycle.singleTask

    <activity android:name=".OtherActivity"
                      android:launchMode="singleTask"
                      android:taskAffinity="cn.edu.zafu.lifecycle.singleTask"
                >
            </activity>

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

    这里写图片描述

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

    singleInstance模式

    该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例。以singleInstance模式启动的Activity在整个系统中是单例的,假如在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

    增加一个Activity,声明如下

    <activity android:name=".singleinstance.SingleInstanceActivity"
                      android:launchMode="singleInstance"
                >
                <intent-filter>
                    <action android:name="cn.edu.zafu.lifecycle"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>

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

    Intent intent = new Intent();
    intent.setAction("cn.edu.zafu.lifecycle");
    startActivity(intent);

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

    这里写图片描述

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

    对以上内容的总结就是

    SingleInstance模式启动的Activity在系统中具有全局唯一性。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。