android的启动模式是在咱们平常开发中常常用使用到,这个也是在面试用常常问到的一个问题。虽然咱们对他很熟悉,但也会有些地方了解的太全面,所以写篇文章来来总结这方面的知识。文章主要内容来自《android开发艺术探讨》这本书,在文章的最后这本书的网页版本可供查看。 java
standard: 标准启动模式android
<!--系统默认启动方式,不须要指定launchMode值-->
<activity
android:name=".StandardActivity"/>
复制代码
下面内容摘自《android开发艺术探讨》第一章16页底部
git
在standard模式下,谁启动了这个Activity那么这个Activity就运行在它的任务栈中。例如:ActivityA启动了ActivityB(B为标准模式),那么ActivityB就会进入ActivityA的任务栈中。github
启动Activity的时候传入的Context不要是ApplicationContext。若是必定要传,那么必定要设置
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
不然回报下面异常:面试
singleTop: 栈顶复用模式shell
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop"/>
复制代码
singleTask: 栈内复用模式浏览器
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask" />
复制代码
singleInstance: 单实例模式bash
<activity
android:name=".SingleInstanceActivity"
android:launchMode="singleInstance" >
复制代码
生命周期执行:微信
onPause-->onNewIntent-->onResume
复制代码
onNewIntent-->onRestart-->onStart
复制代码
查看activity在栈中的状况,可在控制台输入:adb shell dumpsys activity activities 经过搜索关键字 most recent first 快速定位 留意包名数据结构
任务栈(Task):
Task特色:
Activity指定须要启动的任务栈能够用过在配置文件中添加taskAffinity属性来实现。
TaskAffinity特色:
<activity android:name=".TestActivity"
android:launchMode="singleTask"
android:taskAffinity="com.test.singleTask.affinity"/>
<activity android:name=".Test2ActivityC"
android:exported="true"
android:allowTaskReparenting="true"/>
复制代码
下面这段内容摘自Activity启动模式与任务栈(Task)全面深刻记录(下)这篇文章。
假如如今有这么一个需求,咱们的客户端app正处于后台运行,此时咱们由于某些须要,让微信调用本身客户端app的某个页面,用户完成相关操做后,咱们不作任何处理,按下回退或者当前Activity.finish(),页面都会停留在本身的客户端(此时咱们的app回退栈不为空),这显然不符合逻辑的,用户体验也是至关出问题的。咱们要求是,回退必须回到微信客户端,并且要保证不杀死本身的app.这时候咱们的处理方案就是,设置当前被调起Activity的属性为: LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"
其中com.tencent.mm是借助于工具找到的微信包名,就是把本身的Activity放到微信默认的Task栈里面,这样回退时就会遵循“Task只要有Activity必定从本Task剩余Activity回退”的原则,不会回到本身的客户端;并且也不会影响本身客户端原本的Activity和Task逻辑。
一个e-mail应用消息包含一个网页连接,点击这个连接将出发一个activity来显示这个页面,虽然这个activity是浏览器应用定义的,可是activity因为e-mail应用程序加载的,因此在这个时候该activity也属于e-mail这个task。若是e-mail应用切换到后台,浏览器在下次打开时因为allowTaskReparenting值为true,此时浏览器就会显示该activity而不显示浏览器主界面,同时actvity也将从e-mail的任务栈迁移到浏览器的任务栈,下次打开e-买了时并不会再显示该activity。
Taskffinity与singleTask实例:
注: 若是使用我在GitHub上创建的项目测这个功能的时候,请将TestTaskffinityOrAllowTaskRep.zip这个压缩包解压,并导入AS中。这个压缩包是我在测试的时候写的用于跳转androidreview应用的testTask应用。
testTask应用(简称T应用)MainActivity中有一个按钮A,点击按钮会调用androidreview应用(简称A应用)的SingleTaskActivity。下面是两个应用的主要代码。
testTask应用代码:
switch (v.getId()) {
case R.id.mBnt_ForSingleTask:
//跳转androidreview应用SingleToak页面的按钮方法
ComponentName cnForSingleTask = new ComponentName(
"com.hdd.androidreview",
"com.hdd.androidreview.Patterm.SingleTaskActivity");
intent.setComponent(cnForSingleTask);
startActivity(intent);
break;
}
复制代码
androidreview应用代码:
<activity
android:name=".Patterm.SingleTaskActivity"
android:exported="true"
android:launchMode="singleTask"
android:taskAffinity="cmom.han.testbt.testTask">
</activity>
复制代码
从上面的A应用代码配置信息中能够看到taskAffinity属性配置的是T应用的包名。所以SingleTaskActivity会在T的任务中被建立。假如T应用MainActivity的按钮A点击事件中启动了SingleTaskActivity。那么cmom.han.testbt.testTask任务栈中会存在SingleTaskActivity和MainActivity两个Activity。下面是Activity在栈中的信息。
TaskAffinity与allowTaskReparenting实例 T应用的B按钮点击事件代码:
case R.id.mBnt_ForAllowTaskRep:
//allowTaskReparenting模式跳转PattermActivity
intent.setAction("com.hdd.androidreview.PattermActivity");
ComponentName cnForAllowTaskRep = new ComponentName(
"com.hdd.androidreview",
"com.hdd.androidreview.Patterm.PattermActivity");
intent.setComponent(cnForAllowTaskRep);
break;
复制代码
A应用的PattermActivity(简称PActivity)的配置信息:
<activity
android:name=".Patterm.PattermActivity"
android:allowTaskReparenting="true"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="com.hdd.androidreview.PattermActivity"/>
</intent-filter>
</activity>
复制代码
点击T用于的B按钮,会启动A用的PActivity。该Activity的allowTaskReparenting属性为true,那么Activity会被移动到T应用的任务中站建立。当T应用按home返回桌面,再点击A应用;PActivity会被移回A的任务栈中。
T应用调用A应用的PActivity栈内信息
T应用按home返回桌面,在启动A应用栈内信息:
allowTaskReparenting仅限于以standard 和singleTop启动的activity使用
指定Activity的启动模式有两种,一种是在AndroidMenifest.xml中指定
<activity
android:name=".Patterm.SingleInstanceActivity"
android:launchMode="singleInstance">
复制代码
另一种是经过Intent来指定
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setClass(context, CycleActivity.class);
context.startActivity(intent);
复制代码
注:intent指定的优先级大于xml中指定的优先级;若是两个方式都指定了启动方式,那么系统会以intent指定的启动方式为准。
FLAG_ACTIVITY_NEW_TASK
等同于在xml中配置了singleTask启动码模式
FLAG_ACTIVITY_SINGLE_TOP
等同于在xml中配置了singleTop启动码模式
FLAG_ACTIVITY_CLEAR_TOP
singTask自带该标记。这个标记会清除同一个任务栈中目标Activity之上的Activity。 若是目标Activity采用了standard启动模式,可是任务栈中已经存在了Activity的实例。那么系统会清除任务中该实例以及它上面的Activity,而且会从新建立一个目标Activity实例放入栈顶。
启动Activity有两种,一种为显示调用
Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);
复制代码
另外一种为隐式调用要配置清单文件
<!--隐式调用-->
<activity
android:name=".Patterm.PattermActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="com.hdd.androidreview.asdf" />
<category android:name="com.hdd.123456" />
<!--比就加上,不然会报错-->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
复制代码
Activity跳转代码
Intent intent = new Intent();
intent.setAction("com.hdd.androidreview.asdf");
//category非必须指定。若是要指定,一点要和清单文件中填写的一至
// intent.addCategory("com.hdd.123456");
context.startActivity(intent);
复制代码
1. action
<activity
android:name=".Patterm.PattermActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="com.hdd.androidreview.asdf" />
<action android:name="com.hdd.androidreview.qwer" />
<action android:name="12345678" />
<!--必须加上,不然会报错-->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
//java代码
Intent intent = new Intent();
intent.setAction("12345678");
context.startActivity(intent);
复制代码
2. category
Intent intent = new Intent();
//必须指定一个并匹配成功
intent.setAction("com.hdd.androidreview.asdf");
//category非必须指定。若是要指定,一点要和清单文件中填写的一至
// intent.addCategory("com.hdd.123456");
context.startActivity(intent);
复制代码
3. data
data的匹配规则和action相似,若是过滤规则中定义了data,那么intent中必需要匹配data。
data有mimeType和URL两部分组成
mimeType为媒体类型
: image/jpeg、audio/mpeg4-generic以及video/*等。
URL数据结构为:
<scheme>://<host>:<prot>/[<path>|<pathPrefix>|<pathPattern>]
复制代码
例如:
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
复制代码
Scheme:URL的模式,好比http、file、content等;若是URL没有指定scheme,这个URL是无效的。
Host:URL主机名,好比www.baidu.com,若是未指定,URL无效。
Port:RUL端口号,好比80,只有scheme和host指定了port才有意义。
Path:表示完整的路径信息。 PathPattern:表示完整路径信息,里面能够包含通配符。 PathPrefix:表示路径的前缀信息
data在定义的时候有两种状况须要注意
非完整写法,及只指定了mimeType或者只指定了URL。
注:若是只指定了URL,系统会默认设置mimeType的值为content和file
<!-- 隐式调用,只配置URL -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必须加上,不然会报错 -->
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
</activity>
<!-- 隐式调用,只配置mimeType· -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必须加上,不然会报错 -->
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="audio/mpeg" />
</intent-filter>
</activity>
复制代码
java代码:
//intent指定只配置了URL的data
intent.setAction("12345678");
intent.setData(Uri.parse("http://www.baidu.com"));
context.startActivity(intent);
//intent指定只配置了mimeType的data
intent.setAction("12345678");
intent.setType("audio/mpeg");
context.startActivity(intent);
复制代码
完整写法
注:若是intent指定的为完整的data,必需要使用setDataAndType(),由于setData()和setType()会彼此清楚对方的值。
<!-- 隐式调用 -->
<activity android:name=".RegulationActivity">
<intent-filter>
<action android:name="12345678" />
<!-- 必须加上,不然会报错 -->
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="audio/mpeg"
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
</activity>
复制代码
java代码:
//intent完整的data
intent.setAction("12345678");
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "audio/mpeg");
context.startActivity(intent);
复制代码
还有一种特殊写法:
//写法1
<intent-filter>
<data
android:mimeType="audio/mpeg"
android:host="www.baidu.com"
android:scheme="http" />
</intent-filter>
//写法2
<intent-filter>
<data android:mimeType="audio/mpeg" />
<data android:scheme="http" />
<data android:host="www.baidu.com" />
</intent-filter>
复制代码
上面的两个写法上在使用效果上是同样的。
第一种,使用intent的resolveActivity
ComponentName componentName = intent.resolveActivity(context.getPackageManager());
if (componentName != null)
context.startActivity(intent);
else
Toast.makeText(context, "匹配不成功", Toast.LENGTH_SHORT).show();
复制代码
第二种,使用PackageManager的resolveActivity
PackageManager packageManager=context.getPackageManager();
ResolveInfo resolveInfo = packageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY);
//判断是否匹配成功
if (resolveInfo != null)
context.startActivity(intent);
else
Toast.makeText(context, "匹配不成功", Toast.LENGTH_SHORT).show();
复制代码
上面的代码中返回ResolveInfo不是最佳的Activity信息,而是全部匹配成功的Activity信息。resolveActivity()填写的第二个参数必须是MATCH_DEFAULT_ONLY。具体缘由能够查看《android开发艺术探索》第一章34页,这里就不解释了。
用了3天终于写完了!其实写这篇文章是为了复习android的基础知识,在复习过程当中增长了对Activity启动模式以及Activity任务栈中的状态的了解。文章中四个启动模式中最麻烦的模式我的认为是singTask,最有意思的为Taskffinerty这个标签的使用,本身能够写个demo或者使用我在GitHub上的项目测试。