一,Activity生命周期全面分析
(1)onCreate()
表示Activity正在被创建,主要做初始化工作例如加载界面布局资源,初始化所需数据。
(2)onRestart()
当前Activity 的 onPause()和onStop()被执行了 ,之后又回到了这个Activity会被调用
(3)onStart()
当前Activity已经可见,但是还没出现在前台及还无法和用户进行交互(也可理解为已经显示但用户还看不到)
(4)onResume()
当前Activity已经可见也可以与用户进行交互,与onStart()的区别在于 onResume处于前台,onStart()处于后台
(5)onPause()
当前Activity表示处于暂停状态,之后会调用onStop(),这里可以做一些不太耗时的操作,例如停止动画,存储数据等,这里要注意只有当前Activity走完onPause()方法 新的Activity才会执行onResume(极端情况下也有可能出现走到Pause方法立刻回到当前activity这个时候会走onResume)
(6)onStop()
当前Activity即将停止,可以做一写回收工作尽量避免过多的耗时操作
(7)onDestroy()
当前Activity即将被销毁,可以做回收工作和最终的资源释放
- 注:当新打开的Activity主题为透明,则旧的Activity不会调用onStop()
问题:为什么 当前Activity走完onPause()方法 新的Activity才会执行onResume()方法?
答:启动Activity的请求会由Instrumentationlai来处理,它通过Binder向AMS(ActivityManagerService)发起请求,AMS内部维护一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用,在ActivityStack中的resumeTipActivity-InnerlLock方法中 会判断栈顶的Activity 需要onPause后新的Activity才可以启动,启动后ActivityThread中的ApplicationThread调用scheduleLaunchActivity方法完成新的Activity的onCreate,onStart,onResume方法。
二,异常情况生命周期分析
- 情况一:资源相关的系统配置发生改变,导致Activity被杀死并且重新创建
例如横竖屏切换如果不做特殊处理,Activity就会被销毁并且重新创建,这个时候当销毁的时候会调用onSaveInstanceState()方法,重新创建会调用onRestoreInstanceState()。在这两个方法中系统会自动做一些恢复工作,例如文本框用户输入的数据,listview滚动的位置等。
系统自动恢复View工作的原理:
当Activity被意外终止后,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托window去保存数据,接着window再委托顶级的容器view去保存数据,顶级容器是一个ViewGroup一般来说很可能是DecorView 之后顶层容器再去一一通知它的子元素来保存数据,是一种委托思想上层委托下层,android中view绘制,事件分发都是类似的思想。
保存时我们会在onSaveInstanceState中进行操作,当获取的时候我们可以在onCreate或者onRestoreInstanceState()中获取。
二者区别:
当onRestoreInstanceState被调用,其参数一定有值,不用额外去判断空值。onCreate就需要先去判断其参数是否为空再进行后续操作。
系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用。”
- 情况二:资源内存不足导致低优先级的Activity被杀死
Activity优先级排序(由高到低)
1.前台Activity:正在和用户交互的Activity,优先级最高
2.可见非前台Activity:例如弹出对话框的Activity·,可见但是无法和用户直接交互
3.后台Activity:已经被暂停的Activity,比如执行了onStop()
- 为Activity设置configChanges属性
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize" />
参数说明:
“mcc“ 移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。
“mnc“ 移动网号,在一个国家或者地区中,用于区分手机用户的服务商。
“locale“ 所在地区发生变化,一般指切换系统语言
“touchscreen“ 触摸屏已经改变。(这不应该常发生,可以忽略)
“keyboard“ 键盘模式发生变化,例如:用户接入外部键盘输入。
"keyboardHidden" 键盘的可访问性发生变化——例如:用户发现了硬件键盘。
“navigation“ 导航型发生了变化。(这不应该常发生。)
"screenLayout" 屏幕布局发生变化——这个会导致显示不同的Activity。
"fontScale" 字体缩放因子发生变化——用户选择了新的字体大小。
"uiMode" 当UI模式发生改变的时候——当用户放置设备到桌子或/汽车或夜间模式改变的时候可以引起UI模式变化。阅读UiModeManager。在API级别8时引入。
"orientation" 屏幕方向发生变化——用户旋转了屏幕。注意:如果应用程序的目标API级别是13或更高(通过属性minSdkVersion和属性targetSdkVersion声明),你也需要声明配置项screenSize,因为这将在设备选择肖像和屏幕方向时发生改变。
"screenSize" 当前可用屏幕大小发生变化。这代表一个当前可用大小的变化,和当前的比率相关,因此当用户选择不同的画面和图像,会发生变化。然而,如果你的程序目标API级别是12或更低,你的Activity总是会自己处理这个配置变化(这个变化不会引起Activity的重启,甚至在Android 3.2或更新的设备上)。在API级别13里加入的。
"smallestScreenSize" 物理屏幕大小的变化。不管方向的变化,仅仅在实际物理屏幕打包变化的时候,如:外接显示器。这个配置项的变化引起在smallestWidth configuration里的变化。然而,如果你的程序目标API级别是12或更低,你的Activity将自己处理这个变化(这个变化不会引起Activity的重启,甚至在Android 3.2或更新的设备上)在API级别13里加入的。
三,启动模式
-
Activity的LaunchMode
(1)standard标准模式
每次启动都会重现创建一个新的实例,不管这个实例是否存在,被创建的实例的生命周期符合典型情况下的Activity的生命周期。
当我们用ApplicationContext去启动standard模式的Activity回报错,原因是,该模式下之后在启动的Activity会自动默认进入启动他的Activity所属的任务栈中,所以非Activity类型的Context并没有所谓的任务栈所以就会产生问题,解决这个问题可以通过制定FLAG_ACTIVITY_NEW_TASK标记为,这样会创建一个新的任务栈,这种情况下相当于以singleTask模式启动的。
(2)singleTop:栈顶复用模式
如果启动的Activity位于任务栈的栈顶,则不会重新创建,同时会调用他的onNewIntent()方法,通过此方法的参数可以获取到当前请求的信息。如果新的Acvtivity已经存在但是不在栈顶,那么新的Activity仍然会重新创建。
(3)singleTask栈内复用模式
只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,也会回调起onNewIntent() 。并且将其调到栈顶,在其上方的对象都会被销毁。具体来说首先系统乎现寻找是否存在该Activity素偶需要的任务栈,如果不存在先建立任务栈,在创建该Activity的实例并存放在栈中,如果存在当前Activity所需要的任务栈,则要看是否在这个栈中存在其实例,如果存在则将其调到栈顶并且调用onNewIntent()方法,如果不存在则创建其实例,并将其压入栈中。
(4)singleInstance单例模式
加强版的singleTask模式,这种模式的Activity会单独存储在一个任务战中,后续均不会产生新的实例除非任务栈被系统销毁 -
任务栈:TaskAffinity
默认情况下,所有的Activity所需要的任务栈都为该应用的包名,我们可以为每一个Activity指定单独不同的任务栈,设置TaskAffinity属性,TaskAffinity主要和singleTask或者allowTaskReparenting属性配对使用
另外任务栈主要分为前台任务栈和后台任务栈,后台任务栈的Activity处于暂停状态,yoghurt可以通过切换将后台任务栈再次调到前台。 -
如何设置启动模式
通过AndroidMenifest指定启动模式
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask"/>
通过标志位指定启动模式
Intent intent=new Intent();
intent.setClass(this,IndexActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
-
Activity的Flags
(1) FLAG_ACTIVITY_NEW_TASK
指定为singleTask启动模式
(2)FLAG_ACTIVITY_SINGLE_TOP
指定为singleTop启动模式
(3)FLAG_ACTIVITY_CLEAR_TOP
当通过这种方式启动时,在同一个任务栈中所有位于它上边的Activity都要出站,一般会和singleTask启动模式一起出现。在这种情况下,被启动的Activity如果存在实例,那么系统会调用它的onNewIntent,如果被启动的Activity采用standard方式,那么连同它以及它之上的Activity都要出栈,系统会创建新的放入栈顶。singleTask默认会具有此标记效果
(4)FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
等同于android:excludeFromRecents="true"
具有这个标记的不会出现在历史Activity的类表中 -
IntentFilter的匹配规则
android启动Activity分为两种 一种是隐式调用一种是显示调用
系统提供的隐式调用:
Intent intent = new Intent();
//把打电话的动作ACTION_CALL封装至意图对象当中
intent.setAction(Intent.ACTION_CALL);
//设置打给谁
intent.setData(Uri.parse("tel:" + phone));//这个tel:必须要加上,表示我要打电话。否则不会有打电话功能,由于在打电话清单文件里设置了这个“协议”
//把动作告诉系统,启动系统打电话功能。
startActivity(intent);
自定义Activity,并隐式方式启动
<activity android:name=".NextActivity">
<intent-filter>
<action android:name="com.itydl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在<action>标签中我们指明了当前活动可以响应 com.itydl 这个 action,我们可以随便写里面的内容,它的加入表示给我们的Activity添加一个动作,只有带动作的Activity才能被隐式启动。而<category>标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的 Intent 中还可能带有的 category。意图中设置的action必须跟"com.itydl"是完全匹配的,只有<action>和<category>中的内容同时能够匹配上 Intent中指定的 action和 category时,这个活动才能响应该 Intent。我们也道,android.intent.category.DEFAULT 是一种默认的 category,在调用startActivity()方法的时候会自动将这category添加到 Intent中
对于Action的原理是:当StartActivity()运行的时候,该Activity会去系统所有清单文件中找对应的Action("")里面能匹配的Activity,找有没有对应的action与我们所写入的能匹配的,如果有(这里是NextActivity),这样就启动了NextActivity。
Intent intent = new Intent();
intent.setAction("com.itydl");
startActivity(intent);
添加Data
<intent-filter>
<action android:name="com.itydl"/>
<data android:scheme="ydl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
在这里浅显介绍一下系统添加data下面的标签都有哪些:
<data>标签中主要可以配置以下内容。
- android:scheme
用于指定数据的协议部分。 - android:host
用于指定数据的主机名部分。 - android:port
用于指定数据的端口部分,一般紧随在主机名之后。 - android:path
用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。 - android:mimeType
用于指定可以处理的数据类型,允许使用通配符的方式进行指定。只有<data>标签中指定的内容和 Intent 中携带的 Data 完全一致时,当前活动才能够响应该 Intent。不过一般在<data>标签中都不会指定过多的内容,常见的是mimeType和scheme。
intent.setData(Uri.parse("ydl:qwe"));
网友评论