《Android 开发艺术探索》笔记1--Activity的生命周期和模式

2023-11-08

Activity的生命周期和模式思维导图

Activity的生命周期和启动模式.png

Activity的生命周期

image

  • onCreate: 声明周期的第一个方法.做一些初始化的动作,例如setContentView
  • onRestart: 表示Activity重新启动.当界面从不可见变为可见时调用,场景Home键切换,从任务栈返回
  • onStart: 表示Activity正在被启动.Activity为理论可见(取决上层界面是否透明),但不是前台无法操作.
  • onResume: 表示Activity已经可见,并且为前台. 与onStart主要是前台后台,有无焦点的区别
  • onPause: 表示Activity正在停止. 做一些存储数据,停止动画操作.不能太耗时,这会影响新的Activity启动,onPause()必须先执行完,新Activity的onResume()才会执行.
  • onStop: 表示Activity即将停止,可以做稍微重量级的回收工作,同样不能太耗时.
  • onDestroy: 表示Activity即将被销毁. 可以做一些最终的资源释放

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

  • onStart()onStop() 是针对Activity是否可见的角度回调
  • onResume()onPause() 是针对Activity是否位于前台的角度回调的

异常情况下的生命周期

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

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

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

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

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

image

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

  • 当正常情况下onCreate中的Bundle类型参数是为null的. 而onRestoreInstanceState是不会被调用的.
  • 当异常发生后的重建,onCreate,onRestoreInstanceState都会被触发,并且其中的Bundle类型参数都不为null

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

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

关于Activity的优先级的高低

  • 前台Activity–正在和用户交互的Activity,优先级最高
  • 可见但是非前台Activity–比如Activity中弹出了一个对话框,导致Activity可见但是无法操作.
  • 后台Activity–已经被暂停的Activity,比如执行了onStop,优先级最低.

禁止异常重建Activity

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

android:configChanges="orientation|screenSize"

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

  • orientation: 屏幕方向发生了改变,手机旋转
  • screenSize: 屏幕的尺寸信息发生改变,此属性和编译版本有关系,当minSdkVersion和targetSdkVersion均低于13,此选项不会导致界面重启.如果高于那么会重启.

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

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

Activity的启动模式

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

  • standard: 默认模式,就是新进入的压在已存在的界面之上.
  • singleTop: 栈顶复用模式.
  • singleTask: 栈内复用模式
  • singleInstance: 栈内单例模式

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布局中的设置.

限定范围不同

  • xml无法直接为其设置FLAG_ACTIVITY_CLEAR_TOP.
  • 代码添加flag无法添加singleInstance模式.

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

Activity的Flags

  • 影响启动模式的标识位: 可以设定Activity的启动模式

  • 影响运行时的标识位: 可以影响运行的Activity的运行状态

  • FLAG_ACTIVITY_NEW_TASK相对于xml中的singleTask,但是动态的时候需要给intent添加两个标志位,否则无法达到效果,如下

intent3.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent3.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  • FLAG_ACTIVITY_SINGLE_TOP相对于xml中的singleTop
  • FLAG_ACTIVITY_CLEAR_TOP在同一个任务栈中所有位于它上面的Activity都要出栈,此标志一般和singleTask一起出现. 如果启动模式为standard它连同上面的都要出栈,之后系统会创建一个新的实例放入栈顶.

IntentFilter匹配规则

  • 显示调用: 需要指定组件信息如包名,类名.
  • 隐式调用: 不需要指定组件信息, Intent需要匹配目标组件IntentFilter中所设置的过滤信息即可.

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

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

  • action: 本身是字符串. Intent匹配中存在action且必须和过滤规则中的其中一个action相同,那么action就算匹配ok. xml中必须有一个action声明.
  • category: 本身是字符串.Intent匹配中intent可以不存在category,但是如果添加了category那么必须要和定义的intent-filter中的category一致,否则失败. 如果我们在Intent不添加的时候,那么系统会自动为我们添加一个预定义的属性android.intent.category.DEFAULT. xml中需添加这个默认的category属性.
  • data: xml过滤规则中可以不声明,如果声明只要匹配了一个就可以.一般情况下data有两部分组成mimeType和URI. mimeType指媒体类型,URI规则如下:

<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

  • PackageManager的resolveActivity()方法
  • Intent的resolveActivity()方法

使用演示:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《Android 开发艺术探索》笔记1--Activity的生命周期和模式 的相关文章

  • android:应用内计费:错误响应:7:项目已拥有

    我正在学习为我的应用程序实现应用程序内计费 以便人们可以在按下捐赠按钮时捐赠美元 用户可以多次捐赠 即购买的是消耗品 下面的代码来自 TrivalDrive 示例和网上的一些教程 Code IabHelper mHelper static
  • Android + XAMARIN + 强制屏幕保持“纵向”模式(使用 AndroidManifest)

    我在这里阅读了这个问题的一些答案 但不知何故我无法让它发挥作用 我的 AndroidManifest xml 如下所示
  • 将图像从 Android 上传到 GCS

    我正在尝试将图像从 Android 直接上传到 Google 云存储 但API似乎不起作用 他们有一些与 App 引擎相关的 Java 示例 我没有看到任何被证明可以在 Android 上运行的示例 在 Android 上 我尝试使用 js
  • OTP(令牌)应自动从消息中读取

    我正在开发一个 Android 应用程序 其中服务器发送 OTP 用户需要在应用程序中输入此 OTP 才能注册我的应用程序 我想要的是 我的应用程序应该能够自动读取服务器发送的 OTP 我怎样才能实现这个目标 在这方面的任何帮助或指导将不胜
  • 在显示之前在后台加载 Activity

    在将视图切换到该活动之前 是否可以在后台加载新活动 例如 我想要一个被调用并显示启动屏幕的斜杠屏幕活动 当显示此启动屏幕时 将加载下一个活动 当加载完成时 当 onCreate 完成时 启动屏幕活动结束 并显示新的活动 我知道另一个选择是在
  • 使用 hisorian.py 时显示“找不到结束时间”

    我正在尝试收集我的应用程序的电池统计信息 运行指定的所有命令后http developer android com tools performance batterystats battery historian index html ht
  • 如何使用MonkeyDevice.instrument?

    嗨 大家好 我正在尝试从 MonkeyRunner 脚本运行我的测试仪器之一 不幸的是我无法让它工作 我尝试使用不同的参数变量调用 MonkeyDevice instrument 但没有成功 我试过了 设备 MonkeyRunner wai
  • Android 中如何调用 getContentResolver() ?

    我想知道当时的背景getContentResolver 叫做 我有一个这样的场景 我有一个调用方法的活动 AmyFunc B 类 这不是一项活动 所以 在B类中我必须使用getContentResolver 我直接打电话getContent
  • audioTrack play() 只播放一次

    第一次使用Android的AudioTrack 我创建了一个 AndroidAudioDevice 类 我用这个构造函数初始化它 public AndroidAudioDevice constructor Log i Audio const
  • 如何使用 Gmail 帐户对 Android 中的应用程序进行身份验证?

    在 android 中 我如何通过 Gmail 帐户对用户进行身份验证 他们有适用于 android 的 api 或支持吗 谢谢 是的 您可以在 Android 中使用 OAuth 有一篇帖子对此说的很详细 Android 中使用适用于 J
  • Android SDK 中缺少 Gradle(使用 cordova + ionic)

    是的 我搜索了这个 但没有搜索到任何内容 只是有关 ANDROID HOME 路径的信息 但就我而言 我认为这不是真正的问题 当我尝试做的时候ionic build android我收到这个错误 错误 在 android sdk 中找不到
  • Android Facebook SDK - 无法接收访问令牌

    我正在尝试在我的 Android 应用程序中使用 Facebook SDK 这是片段 Facebook myFacebook new Facebook 123456789012345 myFacebook authorize LogInSc
  • 如何在活动之间传递大型集合(主从流)

    背景 我正在实现一个从网络服务读取有关电影信息的应用程序 该网络服务返回有关每部电影的一些信息 标题 日期 海报网址 导演 演员等 该 Web 服务支持分页 因此电影以 100 部为一组加载 执行 这个想法是显示一个包含所有海报的网格 当用
  • 在 Android 应用程序中读取 CSV 文件

    我正在开发一个概念验证应用程序 以便我可以在我正在制作的更大的应用程序中实现该功能 我对 Java 和 Android 开发有点陌生 但希望这个问题不会太简单或太复杂 基本上 我试图从 CSV 文件中读取字符串列表 并使其可用于在应用程序的
  • Android GLSurfaceView 具有可绘制背景

    我有一个带有可绘制对象作为背景的 GLSurfaceView 但是在没有 surfaceView setZOrderOnTop true 的情况下渲染时只有背景可见 我需要避免使用 setZOrderOnTop true 因为在 GLSur
  • Proguard错误android,无法访问jar文件

    这是我第一次使用 progurad 在导出签名的 apk 时出现以下错误 错误 无法访问 jarfile lib proguard jar 我取消注释了 proguard config 行 To enable ProGuard to shr
  • Android:RunOnUiThread 与 AsyncTask

    我相信 Google 建议开发人员使用 AsyncTask 但是 我想知道它与使用 new Thread 然后调用 RunOnUiThread 在性能和内存效率方面有何不同 使用 RunOnUithread 的示例 some code 1
  • Android studio - 如何使用 gradle 中的可执行 jar 文件

    所以我有一个 custom rules xml 文件 我试图在 gradle 中重建 到目前为止 我对其他所有事情都没有问题 但我试图完成的最后一部分是在我使用 gradle 构建过程生成的未签名 apk 上运行一个特殊的 apk 签名工具
  • 如何在android中格式化长整型以始终显示两位数

    我有一个倒计时器 显示从 60 到 0 的秒数 1 分钟倒计时器 当它达到 1 位数字 例如 9 8 7 时 它显示 9 而不是 09 我尝试使用String format B 02d B x 我将 x 从 long 转换为字符串 它不起作
  • 如何使用Android平板模拟器?

    我想开始 Android 平板电脑应用程序开发 我已经使用 1024X600 模拟器创建了一个新的 avd 并启动了 但是当我单击任何应用程序时如何从模拟器返回 是否有任何特定的 api 可用于开发 android 平板电脑应用程序 或者我

随机推荐