android中acitivity上-启动方式和启动模式

一、 activity的启动方式分为两种显示启动和隐式启动

1、显示启动方式:

A:最常见的

MainActivity是当前的activity类,TargetActivity类是需要跳转到的类

startActivity(new Intent(MainActivity.this,TargetActivity.class));


B:通过Intent的ComponentName

ComponentName cn = new ComponentName("MainActivity的全类名","TargetActivity的全类名") ; Intent intent = new Intent() ; intent.setComponent(cn) ; startActivity(intent) ;


C:初始化Intent时指定包名

Intent intent = new Intent("android.intent.action.MAIN"); intent.setClassName("MainActivity的全限定类名","TargetActivity的全限定类名"); startActivity(intent);


2、隐式启动:

通过Intent-filter的Action,Category或data来实现,这个是通过Intent的intent-filter来实现

AndroidManifest.xml中定义被启动类的过滤条件

<activity android:name=".MainActivity"> <intent-filter> <action android:name="my_action" /> <category android:name="my_category" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>


MainActivity.java中启动代码:

Intent intent = new Intent(); intent.setAction("my_action"); intent.addCategory("my_category"); startActivity(intent);


3. 通过包名启动apk

Intent intent = getPackageManager().getLaunchIntentForPackage("第一个启动的apk activity的全类名") ; if(intent != null) startActivity(intent) ;


二、Acitivity的四种启动模式

一、在将启动模式之前必须了解的一些知识:

在正式的介绍Activity的启动模式之前,我们首先要了解一些旁边的知识,这些知识如果说模糊不清,那么在讨论启动模式的时候会一头雾水(笔者亲身感悟)。


1.一个应用程序通常会有多个Activity,这些Activity都有一个对应的action(如MainActivity的action),我们可以通过action来启动对应Activity(隐式启动)。


<action android:name="android.intent.action.MAIN" />

2.一个应用程序可以说由一系列组件组成,这些组件以进程为载体,相互协作实现App功能。


3.任务栈(Task Stack)或者叫退回栈(Back Stack)介绍:


3.1.任务栈用来存放用户开启的Activity。


3.2.在应用程序创建之初,系统会默认分配给其一个任务栈(默认一个),并存储根Activity。


3.3.同一个Task Stack,只要不在栈顶,就是onStop状态:




3.4.任务栈的id自增长型,是Integer类型。


3.5.新创建Activity会被压入栈顶。点击back会将栈顶Activity弹出,并产生新的栈顶元素作为显示界面(onResume状态)。


3.6.当Task最后一个Activity被销毁时,对应的应用程序被关闭,清除Task栈,但是还会保留应用程序进程(狂点Back退出到Home界面后点击Menu会发现还有这个App的框框。个人理解应该是这个意思),再次点击进入应用会创建新的Task栈。


4.Activity的affinity:


4.1.affinity是Activity内的一个属性(在ManiFest中对应属性为taskAffinity)。默认情况下,拥有相同affinity的Activity属于同一个Task中。


4.2.Task也有affinity属性,它的affinity属性由根Activity(创建Task时第一个被压入栈的Activity)决定。


4.3.在默认情况下(我们什么都不设置),所有的Activity的affinity都从Application继承。也就是说Application同样有taskAffinity属性。


<application

android:taskAffinity="gf.zy"

4.4.Application默认的affinity属性为Manifest的包名。



暂时就是这么多了,如果还有不妥的地方我会补充的。接下来我们来正式看Activity的启动模式:


二、Activity启动模式:

1.默认启动模式standard:

该模式可以被设定,不在manifest设定时候,Activity的默认模式就是standard。在该模式下,启动的Activity会依照启动顺序被依次压入Task中:




上面这张图讲的已经很清楚了,我想应该不用做什么实验来论证了吧,这个是最简单的一个,我们过。


2.栈顶复用模式singleTop:

在该模式下,如果栈顶Activity为我们要新建的Activity(目标Activity),那么就不会重复创建新的Activity。




这次我来用代码举例:


<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android=" schemas.android.com/apk "

package="zy.pers.activitytext">

<application

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:roundIcon="@mipmap/ic_launcher_round"

android:supportsRtl="true"

android:taskAffinity="gf.zy"

android:theme="@style/AppTheme">

<activity android:name=".MainActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name=".TwoActivity"

android:launchMode="singleTop">

<intent-filter>

<action android:name="ONETEXT_TWOACTIVITY" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</activity>

<activity android:name=".ThreeActivity">

<intent-filter>

<action android:name="ONETEXT_THREEACTIVITY" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</activity>

</application>

</manifest>

这是我的第一个应用OneText的Mainfest结构,里面创建了三个Activity,我们把第二个Activity的模式设置为singleTop。


每个Activity界面都只有一个显示当前界面名称的TextView和一个用来组跳转的Button,所以应用OneText的功能就是从活动1跳转到活动2,活动2继续跳转活动2,代码就不给大家展示了,都能写出来。




我们发现在我们跳转到TwoActivity之后,点击跳转新的TwoActivity时候,他没有响应。


为了作对比,我们再把TwoActivity设置为standard,看一看效果:




我们发现创建了很多的TwoActivity。


同时我们打印上task的Id(我没有把所有周期方法都打印log):




发现他们全部都是来自一个Task。这个可以过。


应用场景:


开启渠道多,适合多应用开启调用的Activity:通过这种设置可以避免已经创建过的Activity被重复创建(多数通过动态设置使用,关于动态设置下面会详细介绍)


3.栈内复用模式singleTask:

与singleTop模式相似,只不过singleTop模式是只是针对栈顶的元素,而singleTask模式下,如果task栈内存在目标Activity实例,则:


将task内的对应Activity实例之上的所有Activity弹出栈。

将对应Activity置于栈顶,获得焦点。



同样我们也用代码来实现一下这个过程:


还是刚才的那一坨代码,只是我们修改一下Activity1的模式为singleTask,然后让Activity2跳转到Activity3,让Activity3跳转到Activity1:




在跳回MainActivity之后点击back键发现直接退出引用了,这说明此时的MainActivity为task内的最后一个Activity。所以这个模式过。


应用场景:


程序主界面,我们肯定不希望主界面被多创建,而且在主界面退出的时候退出整个App是最好的设想。


耗费系统资源的Activity:对于那些及其耗费系统资源的Activity,我们可以考虑将其设为singleTask模式,减少资源耗费(在创建阶段耗费资源的情况,个人理解-。+)。


4.全局唯一模式singleInstance:

这是我们最后的一种启动模式,也是我们最恶心的一种模式:在该模式下,我们会为目标Activity分配一个新的affinity,并创建一个新的Task栈,将目标Activity放入新的Task,并让目标Activity获得焦点。新的Task有且只有这一个Activity实例。 如果已经创建过目标Activity实例,则不会创建新的Task,而是将以前创建过的Activity唤醒(对应Task设为Foreground状态)




我们为了看的更明确,这次不按照上图的步骤设计程序了(没错,这几张图都不是我画的-。+!)。


我们先指定一下这次的程序:还是这三个Activity,这次Activity3设置为singleInstance,1和2默认(standard)。


然后我们看一下这个效果:




说一下我们做了什么操作:


首先由1创建2,2创建3,然后又由3创建2,2创建3,3创建2,然后一直back,图如下:




还请各位别嫌弃我-。+,图虽然不好看,但是很生动形象。。。。具体说一下:这张图对应着我们上面的程序流程,黄色的代表Background的Task,蓝色的代表Foreground的Task。


我们发现back的时候会先把Foreground的Task中的Activity弹出,直到Task销毁,然后才将Background的Task唤到前台,所以最后将Activity3销毁之后,会直接退出应用。


但是有没有想过这样会出现一个问题,什么问题我们直接看图就好:




我简单说一下这个案例:1,2,3三个Activity,2是singleInstance模式,然后1->2,2->3,之后狂点back,在回到Home界面后点击菜单键,发现首先启动的是2Activity。


简单解释一下:1和3是一个task,2是单独的一个task,在我们2->3后,前台的task又从2的回到了1和3的。所以最后退出的task是2的线程,而如果不是重新启动App。上一次最后关闭的Task是2的,所以。。


所以说singleInstance设置的Activity最好不要设置成中间界面。




以上表示我们关于四种模式的最基本理解,其实有了前面的知识了解之后,我们发现这些其实也不是很难对吧。。。真正比较绕的在后面-。+,注意前方高能:


三、动态设置启动模式

在上述所有情况,都是我们在Manifest中设置的(通过launchMode属性设置),这个被称为静态设置(我们写程序写多了会发现有静态就有动态,而且静态多数在xml设置,动态在java代码设置-。+),接下来我们来看一下如何动态的设置Activity启动方式。


注):如果同时有动态和静态设置,那么动态的优先级更高。


1.关于动态设置与静态设置的理解:

关于这个理解我是看过一篇文章,比较认同里面的思想,所以在这里也总结一下:


静态设置,可以理解为通知别人:就是当我被创建的时候,我告诉你我是通过这种模式启动的。


动态设置,可以理解为别人的要求:别人给我设一个Flag,我就以这种Flag的方式启动。


可能这个没什么用哈,但是仔细想一下这种对程序的思想理解应该是正确的。


2.几种常见的Flag:

我们说的动态设置,其实是通过Intent。对与Intent这个类大家应该不陌生吧,我们刚才在启动Activity的时候就用到这个类了。


如果我们要设置要启动的Activity的启动模式的话,只需要这样:


intent.setFlags(、、、、、);

然后在里面添加对应的Flag就好,那么接下来我们介绍几个常见的Flag(他的Flag太多了,头皮发麻。):


2.1._NEW_TASK


他对应的Flag如下:


Intent.FLAG_ACTIVITY_NEW_TASK

这个Flag跟我们的singleInstance很相似:在给目标Activity设立此Flag后,会根据目标Activity的affinity进行匹配:如果已经存在与其affinity相同的task,则将目标Activity压入此Task。 反之没有的话,则新建一个task,新建的task的affinity值与目标Activity相同。然后将目标Activity压入此栈。


其实简单来说,就是先看看需不需要创建一个新的Task,根据就是有没有相同的affinity。然后把Activity放进去。


但是此情况和singleInstance有不同,有两点注意的地方:


新的Task没有说只能存放一个目标Activity。只是说决定是否新建一个Task。而singleInstance模式下新的Task只能放置一个目标Activity。

在同一应用下,如果Activity都是默认的affinity,那么此Flag无效。而singleInstance默认情况也会创建新的Task。

这个东西理解起来可能有一些抽象,我们通过一个实例来证明他:


在之前的一些例子中,我们都是在同一应用之间进行跳转,而现在我们进行不同App的Activity相互跳转(其实就是创造一个不同taskAffinity的情况。。。忘了的话见一、4)。


首先,我们需要创建一个新的App——TwoApp,这个App目前只需要一个MainActivity就够了,我们在MainActivity放置一个button,让他跳转到OneApp的TwoActivity。


public void onClick(View v) {

Intent intent = new Intent("ONETEXT_TWOACTIVITY");

startActivity(intent);

}

这是跳转的代码。


现在我们先概述一下我们的流程:我们先打开TwoApp,然后在TwoApp的MainActivity界面跳转到OneApp的TwoActivity。


对于OneApp的设定,我们已经将三个Activity都设置成了standard模式。还是1->2,2->3,3->2。


代码就不上了,这么简单,大家自己也能写出来。效果如下:


为了看的清清楚楚,最开始清空了所有的进程。


现在我们点开TwoApp,


现在只有TwoApp一个进程,


现在我们点开了 OneApp的TwoActivity,但是我们发现他还是只有一个进程,




现在我们在TwoApp的MainActivity跳转到OneApp的TwoActivity,添加_NEW_TASK的Flag。


public void onClick(View v) {

Intent intent = new Intent("ONETEXT_TWOACTIVITY");

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

}

我们再看一下效果,还是跟刚才一样先清空所有的进程,这次效果直接连起来看了:




与上面不同的地方在于,我们新的界面创建在了新的进程中——其实就是OneApp被唤醒了,我们来分析一下为什么会这样:


首先我们会想一下我们上面所学过的一个东西,affinity:我们说这个东西在默认情况下就是App的包名packageName,而OneApp中的TwoActivity默认的affinity就是OneApp的包名。这里能够理解吧。


然后我们说_NEW_TASK情况下,会先查找是否有对应的affinity的task,如果有就不在创建,直接将其放入,反之新建task,所以新建的task就是我们的OneApp的task,我们可以再做一个测试,我们先唤醒OneApp,然后再让TwoApp跳转到OneApp的TwoActivity(有点绕啊。。。),我们看是什么情况:


首先启动OneApp,并跳转到ThreeActivity。


然后启动TwoApp,并跳转到OneApp的TwoActivity。


然后一直点击Back,


我们发现在Two中唤醒One的TwoActivity,同样是被放入了OneApp的默认Task中。


关于_NEW_TASK我们就说这么多吧。


2.2._SINGLE_TOP


该模式比较简单,对应Flag如下:


Intent.FLAG_ACTIVITY_SINGLE_TOP

次Flag与静态设置中的singleTop效果相同,所以请见二、2.


2.3._CLEAR_TOP


这个模式对应的Flag如下:


Intent.FLAG_ACTIVITY_CLEAR_TOP

当设置此Flag时,目标Activity会检查Task中是否存在此实例,如果没有则添加压入栈,


如果有,就将位于Task中的对应Activity其上的所有Activity弹出栈,此时有以下两种情况:


如果同时设置Flag_ACTIVITY_SINGLE_TOP,则直接使用栈内的对应Activity,

没有设置。。。。。。。,则将栈内的对应Activity销毁重新创建。

关于这个Flag,我们发现他和singleTask很像,准确的说,是在_CLEAR_TOP和_SINGLE_TOP同时设置的情况下,就是singleTask模式。


而唯一不同的一点就在于:他会销毁已存在的目标实例,再重新创建。这个我们通过打印一下生命周期就好。


这次我们只用OneApp就好了,还是1->2,2->3,3->2,这次我们将2的Flag设置为_CLEAR_TOP,看一下TwoActivity的生命周期。




我们的流程如下:1->2 2->3 3->2 2->3 3->2 back back 然后就退出了,这说明在Task内2上面的3的确被弹出栈了。


然后我们再看一下2的日志:




我想在日志图片上面标注的很清楚了,我只截取了一部分日志,我们质疑3->2时候先销毁,后创建。




好,现在我们同时加上_SINGLE_TOP的Flag。


效果相同,我们只看log:




很明显,在3->2的时候,TwoActivity调用了onRestart方法,也就是栈顶复用了。


这个Flag过。


参考文章: blog.csdn.net/zy_jibai/

发布于 2020-05-14 00:48

文章被以下专栏收录