我要做 Android 之启动模式

Q:如何避免配置改变时Activity重建?

这些部分都要归并到 Activity 异常情况下的生命周期分析情况。

一:由于资源相关配置发生改变,导致 Activity 被杀死或重新创建

例如屏幕发生旋转:横竖屏切换的时候,系统会自动调用 onSaveInstanceState 来保存切换时的数据,接着销毁当前的 Activity,然后重新创建一个 Activity,在调用 onRestoreInstanceState 恢复数据。

  • onSaveInstanceState --> onPause(不定)--> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume

为了避免由于配置改变导致Activity重建,可在 AndroidManifest.xml 中对应的 Activity 中设置android:configChanges="orientation|keyboardHidden|screenSize"。此时再次旋转屏幕时,该Activity不会被系统杀死和重建,只会调用 onConfigurationChanged。因此,当配置程序需要响应配置改变,指定configChanges属性,重写 onConfigurationChanged 方法即可。

由于系统资源不足,导致优先级低的Activity被回收

Activity优先级排序:

  • 前台可见 Activity > 前台可见不可交互 Activity(前台Activity弹出Dialog) > 后台 Activity(用户按下Home键、切换到其他应用)

当系统内存不足时,会按照Activity优先级从低到高去杀死目标 Activity 所在的进程。

若一个进程没有四大组件在执行,那么这个进程将很快被系统杀死。当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并且后续通过 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数据,如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,比较好的方法是将后台工作放入Service 中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

1.当 app 处于后台被系统回收时,app 的进程被杀死了,Activity 也被回收了,而 app 的 task 和 activity 栈以及相应的 intent 和数据会被系统保存起来。当 app 被切回前台时,系统会恢复 task 和 activity 栈以及相应的 intent和数据。 2.不要在 Application 类和全局单例类中存放数据,会导致 app 无法正确恢复状态。运行时的临时数据应存放在SharedPreference、临时文件或数据库中 3 Activity 之间传数据应该用系统提供的 intent 机制。

Activity四种启动模式

一:设置 Activity 启动模式的方法.(两种)

(1)在 AndroidManifest.xml 中给对应的 Activity 设定属性,android:launchMode="standard | singleInstance | single Task | singleTop"

(2)通过标记位来设定,方法是 intent.addFlags(Intent.xxx)

二:Activity 的四种 LaunchMode

(1)standard:标准模式,默认

含义:每次启动一个 Activity 的时候都会创建一个新的实例,不论活动栈中是否已经存在当前 Activity 的实例。

tip:使用 ApplicationContext 去启动 Standard 模式 Activity 会报错。因为 standard 模式的 Activity 会默认进入启动它的活动的任务栈,但是非 Activity 的 Context 没有所谓的任务栈。所以会导致错误。

(2)singleTop:栈顶复用模式

含义:如果新 Activity 已经位于任务栈的栈顶,就不会重新创建,并且回掉 onNewIntent(intent) 方法。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。**例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。如果 D 具有默认的 "standard" 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。但是,如果 D 的启动模式是 "singleTop",则 D 的现有实例会通过 onNewIntent()接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 A-B-C-D。但是,如果收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即便其启动模式为 "singleTop"也是如此。

(3)singleTask:栈内复用模式

含义:只要该 Activity 存在在一个任务栈中存在,都不会重新创建它的新实例,且系统会通过调用现有实例的onNewIntent()方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。如果不存在,系统会先寻找是否存在需要的栈,如果不存在该栈,就创建一个任务栈,并把 Activity 放进去;如果存在,就会把 Activity 创建到已经存在的栈中。

(4)singleInstance:单实例模式

含义:具有此模式的 Activity 只能单独位于一个任务栈中,且此任务栈中只有唯一一个实例。由此 Activity 启动的任何 Activity 均在单独的任务中打开。

三:常用的可设定 Activity 启动模式的标记位

(1)FLAG_ACTIVITY_SINGLE_TOP : 对应 singleTop 启动模式。

(2)FLAG_ACTIVITY_NEW_TASK :对应 singleTask 模式。

为什么要研究启动模式

  1. 有时候我们的App需要生成给其他App调用的Activity,例如浏览器应用,照相机应用

  2. 解决生成重复页面等等Bug

  3. 任务栈过深的时候,避免一直按返回键也退不回想要的页面

四:四种启动模式的应用场景

singleInstance :

单一实例模式,整个手机操作系统里面只有一个实例存在。不同的应用去打开这个activity 共享公用的同一个activity。他会运行在自己单独,独立的任务栈里面,并且任务栈里面只有他一个实例存在。应用场景:呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。或者你确定你需要使Activity只有一个实例。

singleTop : 消息推送

(1)通知栏弹出 Notification,点击 Notification 跳转到指定 Activity,但是如果我现在页面就停留在那个指定的Activity,会再次打开我当前的 Activity ,这样返回的时候回退的页面和当前页面一样,感官上就会很奇怪。(生成了重复页面的 bug)

(2)登录的时候:登录成功跳转到主页,按下两次登录按钮,生成了两个主页。一些有启动延迟的页面(往往是动画,网络造成)也会有这样的情况。(我自己写小项目的时候就遇到过,因为网络卡顿,所以同一个按钮点击了好几次,这样在回退的时候返回了好几个相同页面,感受很不舒服,不过当时说实话没想这么多。最近在复习这些知识点才意识到问题。)

(3)耗时操作返回页面。还有一种场景 从activity A启动了个 service 进行耗时操作,或者某种监听,这个时候你home 键了,service 收集到信息,要返回 activityA 了,就用 singleTop 启动,实际不会创建新的 activityA,只是resume 了。不过使用 standard 又会创造2个A的实例。

singleTask : 调用的时候清空这个 activity 任务栈上面所有的activity。(注意重点)

做浏览器、微博之类的应用,比如其他App需要打开我们的浏览器页面,就可以配置他为singleTask模式,保证他只有一个唯一实例,

详细了解可以看这篇文章:https://blog.csdn.net/zivensonice/article/details/51569502

Q:onNewIntent() 的调用时机是什么呢?

a01.png

当 activity (假设为 A) 的 launchMode 为 singleTop 且 A 的实例已经在 task 栈顶,或者 launchMode 为 singleTask 且 A 的实例已在 task 栈里 (无论是栈顶还是栈中),再次启动 activity A 时,便不会调用 onCreate() 去产生新的实例,而是调用 onNewIntent() 并重用 task 栈里的 A 实例。

如果 A 在栈顶,那么调用顺序依次是 A.onPause() –> A.onNewIntent() –> A.onResume()。A 的 launchMode 可以是 singleTop 或者是 singlTask.

如果 A 不在栈顶,此时它处于 A.onStop() 状态,当再次启动时,调用顺序依次是 [A.onStop()] –> A.onNewIntent() –> A.onRestart() –> A.onStart() –> A.onResume()。A 的 launchMode 只能是 singleTask。

onNewIntent(Intent intent)的调用时机

在上述情况中,可以看到,每次重用activity实例的时候,都会调用onNewIntent()。

重用实例的生命周期如下:
onNewIntent() –> onRestar() –> onStart() –> onResume()

注意:重用实例时如果涉及到通过Intent传递参数,那么在onNewIntent中一定执行一次setIntent(intent),否则会导致通过getInent()拿到的intent是旧数据,而不是最新的参数。

五:IntentFilter匹配规则

原则: (1)一个intent只有同时匹配某个 Activity 的 intent-filter 中的 action、category、data 才算完全匹配,才能启动该 Activity。 (2) 一个Activity可以有多个 intent-filter,一个 intent 只要成功匹配任意一组 intent-filter,就可以启动该 Activity。

a. action 匹配规则:

  • 要求 intent 中的 action 存在且必须和intent-filter中的 其中一个 action相同。如果 InterFilter 中有 action 的话那么在 intent 中代码设置也必须要有 action。

  • 区分大小写。

b. category 匹配规则:

  • intent中的category可以不存在,这是因为此时系统给该Activity 默认 加上了 < category android:name="android.intent.category.DEAFAULT" /> 属性值。

  • 除上述情况外,有其他category,则要求 intent 中的 category 和 intent-filter 中的所有 category 相同。

c. data 匹配规则:

  • 如果 intent-filter 中有定义 data,那么 Intent 中也必须也要定义 date。

  • data 主要由 mimeType (媒体类型)和 URI 组成。在匹配时通过 intent.setDataAndType(Uri data, String type) 方法对date进行设置。

  • 要求和 action相似:如果没有指定 URI,默认值为 content 和 file; 有多组 data 规则时,匹配其中一组即可。

IntentFilter

IntentFilter 的意思就是意图过滤器, 当我们隐式的启动系统组件的时候,就会根据 IntentFilter 来筛选出合适的进行启动。

现在我们知道了可以在 Intent 启动的时候对应设置 Action、Category 、DataAndType,这里设置的是为了过滤的时候对应 IntentFilter 匹配 Action、Category 、data。

除开过滤广播的的 IntentFilter 可以在代码中创建外,其它组件的都得在 AndroidManifest.xml 中给设置。

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
intentFilter.addCategory(Intent.CATEGORY_APP_EMAIL);
intentFilter.addDataType("video/mpeg");
Reciver reciver = new Reciver();
registerReceiver(reciver, intentFilter);

这是代码设置的方法,我们来看看在 xml 文件中的设置方法

<activity  android:name=".Activity_B"    
           android:label="@string/title_activity_activity__b"
           android:launchMode="singleInstance">  
  <intent-filter> 
    <action android:name="android.intent.action.ANSWER" />    
    <category android:name="android.intent.category.APP_EMAIL" />        
    <data  android:host="www.mathiasluo.com"   
           android:scheme="http" />  
  </intent-filter>
</activity>

在不断的复习总结中成长,每次再看一遍不仅会加深理解,而且还会发现之前没有注意到的细节,又会发现之前没有注意到的细节(话说哪来这么多细节,,,)

未来没人能看破,作为一个大学生,正在迷茫期。只能像 李莎莎 这首歌唱的一样,沉重的欲望拖着向前走。

愿我们成为真实的自己。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容