Android开发艺术探索笔记(2)- Activity的启动模式

之前对Activity的启动模式的了解都是百度的,知识点零零散散。在这本书中,作者对Activity的启动模式总结的挺全面,这也是我对这本书做读书笔记的原因之一。


Activity的启动模式

简单的来说,1个Standard和3个Single。

(1)Standard
标准模式,也是Activity的默认启动模式。比较普通,就是启动一个Activity,就会生成一个实例压入任务栈中。Finish一个Activity,就将Activity弹出栈。如有3个Activity,启动模式均是Standard,任务栈中有ABC,如果你再启动一个C,任务栈就变成ABCC,再启动一个B,就变成ABCCB,返回键,就将B弹出栈,又变回ABCC了。再啰嗦两句,任务栈采用的是“后进先出”的方式,也就说先进栈的后出栈。

(2)SingleTop
SingleTop模式也很简单,我先举个栗子。如有3个Activity(A,B,C),A的启动模式是Standard,B,C
的启动模式是SingleTop,任务栈照样有ABC,如果你再启动一个C,因为C已经在栈顶了,所以不再创建一个新的实例,只是会调用C的onNewIntent方法,此时的栈照样是ABC,如果我们再启动一个B,因为B不在栈顶,所以会新建一个B的实例压入栈中,so此时的任务栈是ABCB,返回键,任务栈就变回ABC了。由栗子可以得出,SingleTop模式的理解是,如果将要启动的Activity已经存在而且处于栈顶,那么不会重新创建实例,只是会调用Activity的onNewIntent方法。即使存在,但是不在栈顶,照样会重新创建实例压入栈顶。

(3)SingleTask
SingleTask模式稍微复杂点,但是掌握其原理,其实吧,也就那么回事。
此时分两种情况,单个任务栈和多个任务栈。

1)单个任务栈
原理很简单,如果任务栈中已经存在要启动的Activity,由于这个模式有clearTop效果,会将该Activity以上的Activity都弹出栈,调用该Activity的onNewIntent方法。如果不存在,就新建一个实例压入栈中。如有3个Activity(A,B,C),A,C的启动模式是Standard,B的启动模式是SingleTask(但是没有设置TaskAffinity),任务栈照样有ABC,如果现在再启动B,那么会将B上面的C给弹出栈,调用B的onNewIntent方法。此时的任务栈就只剩下AB了。

2)多个任务栈
书中还说了SingleTask模式一般会和TaskAffinity一起用,TaskAffinity英文翻译为密切关系(亲和性),在Android是指定任务栈的名字。Activity的默认任务栈是包名,如果你指定了TaskAffinity的话(不能和包名相同),启动指定了TaskAffinity的Activity就会找是否有指定的任务栈,如果没有就新建一个任务栈,并压入栈中。多个任务栈和单个任务栈的SingleTask原理差不多,作者举了一个栗子说明。
如存在两个栈(1,2),1栈中有AB,2栈中有CD,如果CD的启动模式均是SingleTask,如果B启动D的话,就会将2栈的CD全部挪到1栈中,此时的任务栈只剩下1栈,1栈变成了ABCD了。

B启动D

那如果B启动是C呢,情况会变得不一样,因为CD都有clearTop效果,所以C会将D给弹出去,照样只剩下1栈,不过此时的1栈变成了ABC了。

B启动C

(4)SingleInstance
SingleInstance模式,顾名思义,就是自己独立一个栈,这是加强版的SingleTask,具有SingleTask的特性。除了这个栈被销毁,以后重复启动具有SingleInstance启动模式的Activity,都不会重新创建新的实例。(但是我有个疑问,那么这个栈的名字是什么??现在我还不清楚,希望知道的读者,指出一下,谢谢!!)

以上4种Activity的启动模式理解完了。下面看看Activity的标志位Flags。


Activity的标志位Flags

书中说到常用的标志位大概有4个。

FLAG_ACTIVITY_NEW_TASK
这个标志位同xml定义SingleTask启动模式。

FLAG_ACTIVITY_SINGLE_TOP
这个标志位同xml定义SingleTop启动模式。

FLAG_ACTIVITY_CLEAR_TOP
这个标志位具有clearTop效果了。一般和FLAG_ACTIVITY_NEW_TASK联用。如果和FLAG_ACTIVITY_NEW_TASK联用,如果被启动的Activity在目标栈存在,会将其上面的Activity弹出栈,调用被启动的Activity的onNewIntent方法。否则,新建实例,压入栈内。(其实FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK联用,是不是只要FLAG_ACTIVITY_NEW_TASK就行了,因为FLAG_ACTIVITY_NEW_TASK就默认有clearTop效果呀,不是很明白)。另一种情况就是,如果被启动的Activity有FLAG_ACTIVITY_CLEAR_TOP,但是不和FLAG_ACTIVITY_NEW_TASK联用,而且被启动的Activity就是Standard,那么启动Activity时,不仅会clearTop,而且会将自己都弹出栈,然后重新创建一个实例。这点需注意一下。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
这个标志位我是第一次看到,而且很有意思。带有这个标志位启动Activity,这个Activity不会添加到最近应用列表。即我们从最近应用里面查看不到我们启动的这个activity。

书中就说了这4中常见的Flags,我这里还百度了几个常见的。

FLAG_ACTIVITY_CLEAR_TASK
此Activity将变成一个新Task中新的最底端的Activity,所有的之前此Activity实例和包含该实例的Task都会被关闭,这个标识仅仅和FLAG_ACTIVITY_NEW_TASK联合起来才能使用。

FLAG_ACTIVITY_FORWARD_RESULT
如果A需要onActivityResult中获取返回结果,startActivityForResult B,而B只是过渡页,启动C之后就finish掉了,需要在 C 中setResult返回给A就可以用到这个标志。

FLAG_ACTIVITY_NO_HISTORY
如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。
例如A启动B的时候,给B设置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,那么:
A -> B -> C ,启动C 就算 B没有自行finish ,也会变为 AC。

FLAG_ACTIVITY_REORDER_TO_FRONT
如果设置这个标记,新启动的Activity将会被放到它所属task的最前面
例如,假如有一个Task包含4个Activity:A,B,C,D.如果D通过调用startActivity( )来启动B,如果使用了这个标记,B将会排在这个task的最上面,也即现在的顺序变成了A,C,D,B。
如果使用了FLAG_ACTIVITY_CLEAR_TOP,这个标记将会被忽略。

以上4个也是比较常用的,用了flags之后,写程序会轻松很多的。


(3)IntentFilter的匹配规则

读到这里,我才知道,Activity启动有两种方式,一个是显式启动,一个是隐式启动。就例如我们打开系统相机的时候,我们的Intent是这样的写的:

Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(filePath));
startActivityForResult(cameraIntent, CAMERA_OK);

这就是一个典型的隐式启动Activity的方式。(我现在才知道这叫隐式启动,都用了这么久,呵呵~~)

要成功隐式启动一个Activity,就得匹配我们在menifest.xml定义的activity的intent-filter规则。

IntentFilter里面需要匹配3个信息:action、category、data。下面是一个在menifest.xml定义的一个activity:

<activity
    android:name="com.johan.TestActivity"
    android:label="@string/app_name"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="com.johan.testaction1" />
        <action android:name="com.johan.testaction2" />
        <category android:name="com.johan.testcategoty" />
        <category android:name="android.intent.category.DEFAULT" />
        <data mimeType="image/*" />
    </intent-filter>
</activity>

说明2点:
1)一个intent-filter可以有多个action、category、data,Intent要同时匹配3个信息才算匹配一个intent-filter,才能成功启动目标的activity。
2)一个activity可以有多个intent-filter,只要Intent能完全匹配一个intent-filter,就算是成功启动目标activity。

以下是action、category和data的匹配规则:

action匹配

Intent必须有一个action,而且要和在menifest.xml文件中目标activity的intent-filter任何一个”action”匹配,即action匹配成功。如:

Intent intent = new Intent();
intent.setAction("com.johan.testaction1");
//或者
//intent.setAction("com.johan.testaction2");

注意区分大小写。

category匹配

你在Intent中设置了几个category,就要在menifest.xml文件中目标activity的同一个intent-filter能找到目标“catagory”,才算category匹配成功。如:

Intent intent = new Intent();
intent.addCategory("com.johan.testcategoty");

但是如果还设置了一个其他的,如

Intent intent = new Intent();
intent.addCategory("com.johan.testcategoty");
intent.addCategory("com.johan.testcategoty2");

因为在intent-filter中找不到name为com.johan.testcategoty2的“category”过滤规则,所以这个category就不能匹配成功了。

当然,我们可以不设置category,因为startActivity或者startActivityForResult的时候,系统会为Intent设置默认的name为“android.intent.category.DEFAULT”的category。所以我们为了能让activity能被隐式启动,在intent-filter加上默认的category过滤规则。

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

data匹配

data匹配和action匹配规则一样,不过要先了解一下data语法:

<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string"
      />

其实上面data就是分为miniType和URI,android:mimeType以上的都是URI的信息,URI的结构如下:

<scheme>://<host>:<port>/[<path>/<pathPattern>/<pathPrefix>]
/* http://www.baidu.com:80/search/info */
/* content://com.johan.project:200/folder/subfolder/ect */
// 说明
// scheme      : URL的模式,如http、content、file...
// host        : 主机名
// port        : 端口号
// path        : 完整路径
// pathPattern : 完整路径,不过带有*等通配符
// pathPrefix  : 路径前缀(这个还不是很理解,作者也没有详细讲解) 

为了要匹配栗子的data,Intent要这样设置:

intent.setDataAndType(Uri.parse("file://abc"), "image/png");

如果要指定完整的data(既有mimeType,又有URI),只能调用setDataAndType,不能setType再setData,或者setData再setType。作者从源码解析的很清晰,因为我们调用setType方法时,会将data=null,调用setData时,会将type=null。

因为在隐式启动Activity时,可能匹配不成功,系统就会抛出异常。作者还介绍了两种方法去判断Intent是否成功隐式启动Activity。

public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
public abstract ResolveInfo resolveActivity(Intent intent, int flags);

第一个参数就是Intent,第二个参数作者建议我们填MATCH_DEFAULT_ONLY,这个的意思是值启动category的name为“android.intent.category.DEFAULT”的intent-filter。只要这两个方法返回不为null,启动一定成功。

还有一类特别的intent-filter:

<intent-filter>
    <action android:name="android.intent.action.Main" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

这个intent-filter表示这个activity为入口Activity。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值