Activity 小结

Activity是Android系统中的四大组件之一,是可以包含用户界面的组件,主要用于和用户进行交互,一般的一个应用程序可以

包含0个到多个Activity。

1, 生命周期

Activity的生命周期如下所示,


OnCreate方法:首次创建 Activity 时调用,主要执行一些初始化操作,比如调用setContentView方法加载界面布局资源等,

但是不要做一些耗时的操作。

onStart方法: 在 Activity 即将对用户可见之前调用。正在启动,Activity已经可见,但没有出现在前台,还在后台,所以

无法与用户交互.

onResume方法: 在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户

输入焦点,已经到前台并且可见。

 

onPause方法:当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画

以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。

如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接onStop()。

onStop方法:在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)

继续执行并将其覆盖,就可能发生这种情况。

如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接onDestroy()。

onDestory方法:在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(对 Activity 调用了 finish()),

或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 可以通过 isFinishing() 方法区分这两种情形。

 

前面三个方法和后面三个方法其实是一一对应的。onStart和onStop是从Activity是否可见的角度来说明的;onResume和

onPause是从Activity是否位于前台的角度来说明的。并且在一些极端的情况下,后三个方法不一定会被调用。

onRestart方法: 在 Activity 已停止并即将再次启动前调用。

始终后接 onStart方法。当一个Activity被重现可见时,使用它你可以实现一些Activity重新可见时的特殊的处理。

 

常见的几种情况的生命周期回调过程:

1、Activity,第一次启动:onCreate-->onStart-->onReasume

2、打开新Activity或切换到桌面:onPause-->onStop;如果新Activity采用了透明主题,则不会回调onStop

3、返回原Activity:onRestart-->onStart-->onResume

4、按下返回键:onPause-->onStop-->onDestory

5、Activity被系统回收了,再打开生命周期的回调和1过程是一样的

2 异常生命周期

2.1系统配置发生改变引起

以图片资源为例,在Android项目中,为了兼容不同的设备,会在不同目录下放入不同的图片,比如drawable-mdpi、

drawable-hdpi、drawable-land等。设备就会根据实际情况去加载合适的Resoures资源。假设你为横屏与竖屏放入

了不同的图片,当Activity从竖屏旋转至横屏时,Activity就会被销毁并且重新创建。

这种异常终止情况下,系统会调用onSaveInstanceState来保存当前Activity状态。系统重新创建Activity之后,会把

onSaveInstanceState方法所保存的Bundle对象作为参数转递给onCreate和onRestoreInstanceState来恢复以前Activity的状态。

onSaveInstanceState方法一般在onStop之前,与onPause没有确定顺序,可能在onPause之前,也可能在onPause之后。

按下Home键或跳转其它Activity,也会调用onSaveInstanceState;正常销毁时,系统不会调用onSaveInstanceState方法,

onRestoreInstanceState方法一般是在onStart之后调用。

注意:onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定有值的;而onCreate是正常启动的话,

其参数Bundle savedInstanceState为null,需要额外判断。其实,这两个方法都可以进行数据恢复,任选其一就行,官方文

档建议采用onRestoreInstanceState。

在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一定的恢复工作。比如文本框的数据、

ListView滚动的位置等。和Activity一样,每个View都有onSaveInstanceState和onRestoreInstanceState,看一下具体实现,

就能知道系统能够自动为View恢复哪些数据。

关于保存和恢复状态,系统的工作流程:Activity意外终止时,会调用onSaveInstanceState保存数据,然后Activity会委托Window

去保存数据,Window会去委托它上面的顶级容器(ViewGroup,一般来说他很可能是DecorView)去保存数据,最后这顶级容器

会一一通知子元素来保存数据;这是一种典型的委托思想,上层委托下层。比如View的绘制过程、事件的分发等都是采用类似的思想。

对于屏幕旋转,如果不想Activity重新创建,我们可以给Activity指定configChanges属性。这样的话,旋转屏幕,Activity就不会

重新创建,取而代之是系统调用Activity的onConfigurationChanged方法。

2.2内存不足引起

Activity的优先级情况:

a、前台Activity:正在与用户交互的Activity,优先级最高

b、可见但非前台Activity:例如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法与用户交互

c、后台Activity:被暂停的Activity,比如执行了onStop,优先级最低

当系统内存不足时,系统就会根据优先级去杀死目标Activity所在的进程,并会通过onSaveInstanceState和onRestoreInstanceState

来存储和恢复数据,其过程和上一节完全一致。

3 生存周期

3.1全生命周期

全生命周期是从Activity建立到销毁的全部过程,始于onCreate(),结束于onDestroy()。开发者通常在onCreate()中初始化

用户界面, 分配引用类变量,绑定数据控件,并创建服务和线程等Activity所能使用的全局资源和状态,并在onDestroy()中

释放这些资源,并确保所有外部连接被关闭,如网络或数据库的连接等;在一些极端情况下,Android系统会直接终止进程,

而不会先调用onDestroy()。

3.2 可见生命周期

可见生命周期是Activity在界面上从可见到不可见的过程,开始于onStart(),结束于onStop()。在极端情况下,系统会直接销毁

一个Activity,而不先调用onStop,即使它处于可见状态。在这个时间里,活动对用户是可见的,但是它有可能不具有焦点,

或者它可能被部分遮住了。在一个Activity完整的生命周期中可能会经过几个Activity可见的生命周期,因为你的Activity可能会

经常在前台和后台之间切换。在极端情况下,Runtime将杀掉一个Activity即使它在可见状态并且并不调用onStop方法。

3.3 前台生命周期

前台生命周期是Activity在屏幕最上层,并能够与用户交互的阶段,并且正在接收用户的输入事件。开始于onResume(),

结束于onPause()。在Activity的状态变换过程中onResume()和onPause()经常被调用,因此这两个方法中的代码应该写得

简单、高效些,以提高性能,以保证在前台和后台之间进行切换的时候应用能够保持响应。当一个新的Actvity启动,或该

设备进入休眠状态,或失去焦点,Activity活跃的生命周期就结束。

4启动模式

4.1 standard

standard:标准模式,系统默认模式。每次创建这种模式的Activity都会重新创建一个新的实例。在这种模式下,谁启动了

这个实例,那么这个Activiyt就运行在启动它的那个Activity所在栈中。

 

注意:当用ApplicationContext去启动standard模式的Acctivity时,会出现错误,错误如下:

Android.util.AndroidRuntimeException:Calling startActivity from outside of anActivity context requires the FLAG_ACTIVITY_NEW_TASK flag,

Is this really what you want?

这是因为非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈。对于这个问题,我们可以为待启动Activity指定

FLAG_ACTIVITY_NEW_TASK标记位,这一样启动的时候就会为它创建一个新的任务栈,但是待启动Activity实际上是以singTask模式启动的。

4.2 singTop

singTop:栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时

它的onNewIntent方法会被回调,这样我们就可以取出当前请求的信息;如果不在栈顶,则会重新创建。

4.3 singTask

singTask:栈内复用模式。在这种模式下,只有Activity在栈中存在,那么多次启动这个Activity都不会重新创建实例;

同时,系统也会回调其onNewIntent方法。

启动这种模式的Activity过程:系统会首先寻找Activity想要的任务栈,如果不存在,就创建一个任务栈,然后把Activity

实例放入栈中;如果存在,这时看栈中是否存在Activity实例,如果存在实例,那么系统就会把Activity调到栈顶并回调

onNewIntent方法以及Activity实例上的实例全部出栈,如果实例不存在,就创建Activity实例并压人栈中。

4.4 singleInstance

singleInstance:单实例模式。除了具有singleTask模式的所有特性外,那就是具有此种模式的Activity只能单独位于

一个任务栈中。

常用标志位:

FLAG_ACTIVITY_NEW_TASK相当于singleTask模式

FLAG_ACTIVITY_SINGLE_TOP相当于singleTop模式

FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,当它启动时,在同一任务栈中所以位于它上面的Activity实例

都会出栈。如果被启动Activity为stardard模式,那么它连同它之上的Activity都要出栈,系统会重新创建新的Activity实例

放入栈中。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的Activity不会出现在历史Activity的列表中。它等同于

在XML中Activity的属性android:excludeFromRecents = "true"。

因此,有两种方式为Activity指定启动模式:

1、通过AndroidMenifest中Activity的属性:android:launchMode

2、通过在Intent中设置标志位

并且第二种方式的优先级高于第一种方式,当两种同时存在时,以第二种方式为准。

TaskAffinity,这个参数标识了Activity所需要任务栈的名字。默认情况下,所有Activity所需任务栈名称为应用包名。

 

5 IntentFilter的匹配规则

启动Activity分为两种,显式调用和隐式调用。显式调用需要明确地指定被启动对象的组件信息,比如包名和类名;

隐式调用不需要明确指定组件信息,需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息。

IntentFilter中的过滤信息有action、category、data。一个过滤列表中可以有多个action、category和data可以有多个。

一个Intent同时匹配action类别、category类别、data类别才算完全匹配。一个Activity中可以有多个intent-filter,

一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。

1、action的匹配规则

action是一个字符串,系统预定了一些action,我们也可以在应用中定义自己的action。这里的匹配指的是action的

字符串值完全相同。一个过滤规则中可以有多个action,那么Intent中的action能够和过滤规则中的任何一个action相同

即可匹配成功。如果Intent中没有指定action,那么匹配失败。另外,action区分大小写。

2、category的匹配规则

category是一个字符串,系统预定了一些category,我们也可以在应用中定义自己的category。Intent可以没有category,

如果Intent包含了category,则必须每一个category都能与过滤规则中的category匹配。系统调用startActivity或startActivityResult

的时候会默认为Intent加上"Android.intent.category.DEFAULT",所以Intent可以没有category。同时,为了我们的Activity能接

收隐式调用,就必须在intent-filter中指定"android.intent.category.DEFAULT"。

 

注意:能用显示启动就不要使用隐式启动,因为在启动过程中,显示启动查找速度一般明显快于隐式启动。

6 进程模式

一般情况下,通常一个应用运行于一个进程中,但是总有例外,

一个应用也可以运行于几个进程中,一个进程也可以运行多个应用。

多进程模式

多进程模式讨论的是一个应用存在多个进程的情况,其实质还是分为结果不同的独立进程。

应用开启多进程模式有两种方法:

第一种:只需在AndroidMenifest文件中为四大组件指定Android:process属性

第二种:通过JNI在native层去fork一个新的进程。

以第一种方法为例:

<activity    
        android:name="com.ryg.chapter_2.MainActivity"    
            android:configChanges="orientation|screenSize"    
            android:label="@string/app_name"    
            android:launchMode="standard" >    
            <intent-filter>    
                <action android:name="android.intent.action.MAIN" />    
    
                <category android:name="android.intent.category._LAUNCHER" />    
            </intent-filter>    
        </activity>    
        <activity    
            android:name=".SecondActivity"    
            android:configChanges="screenLayout"    
            android:label="@string/app_name"    
            android:process=":remote" />    
        <activity    
            android:name=".ThirdActivity"    
            android:configChanges="screenLayout"    
            android:label="@string/app_name"    
            android:process="com.ryg.chapter_2.remote" />  

分析:

当前包名为“com.ryg.chapter_2”;

当SecondActivity启动时,会创建进程名为“com.ryg.chapter_2:remote”的进程;

当ThirdActivity启动时,会创建进程名为”com.ryg.chapter_2.remote“的进程;

同时入口MainActivity,没有为它指定process属性,则会运行在默认进程中,默认进程名为包名。

SecondActivity与ThirdActivity的进程名命名方式不同:前者是简写的命名方式,”:“的含义是指当前进程名前面附加上当前的包名;

后者是完整的命名方式,不会附加包名信息。

注意:进程名以”:“开头的进程属于当前应用的私有进程;而进程名不以”:“开头的进程属于全局进程,也就是说其他应用可以通过

ShareUID的方式与全局进程进行共享数据,比如相互访问对方的私有数据(data目录、组件信息等),还要注意的是签名必须相同。

 

开启多进程模式很简单,但是它会造成如下问题:

1、静态成员和单例模式失效

2、Application会多次创建

3、线程同步机制完全失效(这里指的是多进程间的线程同步)

4、SharedPreferences的可靠性下降

android为每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,也就导致了同一类对象会有多份副本,

所以运行在不同进程间的四大组件不能通过内存来共享数据;这就解释了第一种问题出现的原因。

当一个组件在一个新的进程中启动的时候,由于系统要新的进程,并为其分配独立的虚拟机,这个过程其实就是启动一个应用的过程,

那么自然会重新创建新的Application;这就解释了第二种问题出现的原因。

不同的进程有不同的内存地址,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象;这就解释

了第三种问题出现的原因。

SharedPreferences是通过读写XML文件来实现,并发读写有可能出现问题;这就解释了第四种问题出现的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值