《Android 开发艺术探索》笔记1--Activity的生命

2020-09-02  本文已影响0人  天一方蓝
Activity的生命周期和启动模式.png

Activity的生命周期

image

场景 当界面A打开界面B时的生命周期调用顺序: A的onPause()-> B的onCreate() -> B的onStart() -> B的onResume() -> A的onStop().

异常情况下的生命周期

当系统配置发生改变的时候

Activity就会因为异常情况被销毁并重新创建.例如横竖屏切换,语言切换等.

当异常状态发生的时候. 在界面销毁前会调用onSaveInstanceState()进行当前界面的数据保存,如文本输入的数据,listView滚动的位置等. 在重建后会调用onRestoreInstanceState()进行因为异常重建的原始数据的恢复.

准确的说onSaveInstanceState() 会在onStop()之前执行, 而onRestoreInstanceState会在onStart之后执行.

当屏幕发生旋转时,声明周期调用过程如下:

image

这里我们要清楚,当发生了异常情况下,系统会帮我们自动恢复大部分的数据,但是如果我们想要自己从异常中恢复.那么我们可以通过onCreate()onRestoreInstanceState()中的参数Bundle来进行值得保存.

资源内存不足时低优先级的Activity被杀死

这种场景不好模拟,但是在存储和恢复的过程是与上面的过程一致的.

关于Activity的优先级的高低

禁止异常重建Activity

如果不想Activity重建.可以通过清单文件中对activity标签进行配置.

android:configChanges="orientation|screenSize"

当给一个Activity声明了上述的属性之后,当手机旋转的时候,activity不会重建,也就没有任何声明周期方法的回调, 但是会调用onConfigurationChanged()方法.

所以这两个最好成对出现.

日常开发中我们比较常用的local,orientation,keyboardHidden,uiMode. local为本地语言切换. uiMode界面模式发生切换,如夜间模式(API8中增加)

Activity的启动模式

当我们打开的activity会被系统以任务栈的形式来存储起来.后进先出.当每一个任务栈为空的时候这个栈就会被回收

singleTop

如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时onNewIntent()会被回调.

如果使用此模式,那么在任务栈中栈顶到栈低为CBA的情况下,再次打开C,那么C界面的onCreate()onStart()不会被调用,真正的调用时onPause()–>onNewIntent()–>onResume()

singleTask

如果用栈内复用,当打开C时候,会查询所有的任务栈,如果有任务栈包含C,那么把这个任务栈移动到所有栈的首位,并清除掉这个栈内C到栈顶的其他Activity,最后调用C的onNewIntent()方法. 如果没有那就直接在所需任务栈的栈顶创建C的实例.

这里由于singleTask默认具有clearTop的效果,所以会清除C以上activity的出栈. 这里和具体的启动模式有关.

所需任务栈: 和一个参数有关系,TaskAffinity.这个参数标识了一个Activity所需要的任务栈的名字,默认情况下所有的Activity的所需的任务栈的名字都为应用包名.

这里我们可以通过命令adb shell dumpsys activity测试一下,打开顺序为MainActivity–>ModeSingleTopArc

当我们不指定taskAffinity的所需栈的时候,查看任务栈的结果为:

image.png

如果指定了taskAffinity 这个时候任务栈的状况为:


<activity android:name=".launchmode.ModeSingleTaskAct"

          android:launchMode="singleTask"

          android:taskAffinity=".nishibendan"/>

image

一般情况下TaskAffinity属性一般和singleTask启动模式或者allowTaskReparenting属性配对使用,其他情况下使用没有意义. 另外任务栈分为前台任务栈和后台任务栈,后台任务栈中的activity属于暂停状态,用户可以切换将后台调到前台.

singleInstance

这个模式是加强版的singleTask,除了singleTask具有的属性之外,还具有创建新栈的能力,这个栈只有这一个实例. 就是说如果假设EActivity没有被创建过,那么创建时,首先会创建一个新的任务栈,然后创建实例放入这个新的栈内,然后下一个实例不会和这个EActivity所属栈共存,会创建一个新的栈继续存放.

给Activity添加启动模式

  1. 通过清单文件中activity标签添加android:launchMode="singleTask|singleTop|singleInstance"
  2. 通过代码中startActivity(Intent)中的intent通过addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

优先级 代码设置优先于xml布局中的设置.

限定范围不同

注意: 如果通过代码添加添加Intent.FLAG_ACTIVITY_NEW_TASK和xml中设置singleTask是不一样的.代码动态添加是没有clean_top的效果,看图:

image.png

Activity的Flags

intent3.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent3.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

IntentFilter匹配规则

IntentFilter的过滤信息有action,category,data. 一个Activity可以有多个Intentfilter,只要intent能匹配任意一组intent-filter即可成功启动.

关于每一项的具体匹配规则

<scheme>://<host>:<port>/[path]

如下实例: scheme:表示URI的模式,例如http, file, content.

http://www.baidu.com:80/search/info

最简单的隐式打开

//代码中

Intent intent = new Intent();

intent.setAction("com.test1");

startActivity(intent);

//xml中

<activity android:name=".intentfilter.FilterAct">

  <intent-filter>

      <action android:name="com.test1"/>

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

  </intent-filter>

</activity>

</pre>

一个比较完整的匹配代码

//代码中

  Intent intent = new Intent();

  intent.setAction("com.test1");

  intent.addCategory("com.category1");

//intent.setDataAndType(Uri.parse("content://"),"audio/plain");//如果过滤规则中没有声明URI的属性,那么会有默认值content和file的属性

  intent.setDataAndType(Uri.parse("http://"),"audio/plain");

  startActivity(intent);

//xml中

<activity android:name=".intentfilter.FilterAct">

  <intent-filter>

      <action android:name="com.test1"/>

      <action android:name="com.test2"/>

      <category android:name="com.category1"/>

      <category android:name="com.category2"/>

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

      <data android:mimeType="text/plain"/>

      <data android:mimeType="audio/plain" android:scheme="http"/>

  </intent-filter>

</activity>

在进行data属性匹配的时候尽量使用setDataAndType, 因为源码中setDatasetType会把彼此属性置为null.

Intent-filter匹配规则对于Service和BroadcastReceiver也是同样. 不过建议Service的使用尽量使用显示调用服务.

判断是否有匹配的Intent

使用演示:

ResolveInfo resolveInfo = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);

参数二使用PackageManager.MATCH_DEFAULT_ONLY,意义在仅仅匹配哪些在intent-filter中声明<category android:name="android.intent.category.DEFAULT"/>,只要使用这个标记不返回null,那么startActivity就一定可以打开. 如果不用这个标记就会把没有设置default的匹配出来.从而导致判断失败.因为不含有DEFAULT这个category的Activity是无法接收隐式Intent的

这样如果返回的为null,那就是没有匹配到,如果不为null那就是可以匹配.

参看文章

《Android 开发艺术探索》书集
《Android 开发艺术探索》 01-Activity的生命周期和模式
https://github.com/feiwodev/AndroidDevelopmentArt

上一篇 下一篇

猜你喜欢

热点阅读