关闭 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在系统中具有全局唯一性。

    上一篇返回首页 下一篇

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

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

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

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    SQL汉字转换为拼音的函数

    windows 7系统无法运行Photoshop CS3的解决方法

    巧用MySQL加密函数对Web网站敏感数据进行保护

    MySQL基础知识简介

    Windows7和WinXP下如何实现不输密码自动登录系统的设置方法介绍

    windows 7系统ip地址冲突怎么办?windows 7系统IP地址冲突问题的

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

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