Activity的生命周期和启动模式

2019-10-20  本文已影响0人  李die喋

Activity的生命周期

Activity的生命周期一般分为两部分,一是正常情况下的生命周期,也就是一般就是这样,而是异常情况下的生命周期。

典型情况下

典型情况下是指在有用户参与的情况下,activity所经过的生命周期的改变。这时activity会经历如下生命周期:

onPause的时候可以做一些存储数据(将数据存储到数据库,存储到本地文件等)、停止动画等工作。这些操作不能太耗时(可以开一个线程),因为会影响到新activity的显示。onPause必须先执行完,新activity的onResume方法才会执行

异常情况下

异常情况下是指activity被系统回收或由于当前设备的configuration发生改变而导致activity被销毁重建。

情况1:资源相关的系统配置发生改变

发生这种情况的典型例子就是屏幕处于竖屏时突然被旋转。

我之前写过一个例子,底部导航栏+fragment旋转后三个fragment重叠在一起了。原因就是activity重建,把三个fragment重新按顺序创建并显示了。解决办法就是使用onSaveInstanceState保存当前所在的fragment页面,在旋转后从onRestoreInstanceState()方法中恢复旋转之前所在页面,其他页面重新创建。就是保留之前所在页面的fragment对象。

这两个保存和恢复数据的方法的调用流程:

情况2:资源内存不足导致低优先级activity被杀死

当系统内存不足是就会按照优先级杀死activity所在的进程。

activity的优先级有三种:

如果一个进程中没有四大组件在执行,那么这个进程很快会被系统杀死。因此后台工作不应该脱离四大组件,最好的方法是将后台工作放在Service中从而保证一定优先级。

Activity的启动模式

为什么需要启动模式

在默认情况下,当我们多次启动同一个activity时,系统会创建多个实例并把他们加入到任务栈中,当我们单击back键的时候,这些activity又会从任务栈中依次退回。是一种“后进先出”的栈结构。当栈中无任何activity时,系统就会回收这个任务栈。这是android的默认启动模式。但是多次启动同一个activity,系统重复创建多个实例,这样是很不合理的。因此它提供了启动模式来修改系统的默认行为。

standard 标准模式

系统的默认启动模式。它的启动具有如下特点:

当用ApplicationContext去启动standard模式的activity的时候会报错。因为standard模式下的activity会进入启动它的activity所在的任务栈中,但是非activity类型的context,就像ApplicationContext并没有所谓的任务栈,所以就会有问题。解决问题的方法就是,为待启动的acitivity指定FLAG_ACTIVITY_NEW_TASK标志位,这样activity启动的时候就会为它自己创建一个新的任务栈。这个时候的启动实际是以singleTask启动的。

singleTop 栈顶复用模式

它有如下特点:

singleTask 栈内复用模式

它有如下特点:

例如一个栈中有四个activity从底到顶分别为ADBC,现在要创建D,发现栈中有,根据栈内复用原则,D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法。同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的activity全部出栈,最终栈内情况为AD。

流程如下:


image

singleInstance 单实例模式

它有如下特点:

一个参数:TaskAffinity

在书上看到这的时候对任务栈分为前台和后台不是很理解,android系统会为每一应用程序都设置一个后台栈么?前台栈的东西都移到后台?这样不会太麻烦了吧。。。查了下,事实并不是我想的那样,当前应用通过home键回到主屏幕的时候,之前的前台栈就会变成后台栈。系统会根据这个应用在后台待的时长去判断是否只保留最初的MainActivity,还是从栈顶的activity进行恢复。

这里有两个例子,感觉会加深一些理解:

  1. 应用A启动了应用B的某个activity,若这个acitivty的属性allowTaskReparent = true。那么再次启动B后,此Activity会直接从A的任务栈中转移到B的任务栈中。

这主要就是属性allowTaskReparent的功劳,这个属性大概意思就是允许当前任务从新找一个父母。也就是重新启动的时候可以到另一栈中。

  1. 假设目前有两个栈,前台任务栈的情况为AB,后台任务栈的情况为CD。假设CD的启动模式均为singleTask,现在请求启动D。

结果是后台任务都会被切换到前台,也就是CD任务都到了前台任务栈中,前台任务栈现在的情况为ABCD。

  1. 假设现在有三个activity。activity A的启动模式为standard,activity B和activity C的启动模式都为singleTask,且B C的TaskAffnity的属性都一样。做如下操作:A启动B,B启动C,C再启动A,A再启动B,最后再按两次返回键,结果会如何?

结果是回到了桌面。其实这个问题画个图想想就很简单。A启动B,A因为是standard模式,所以A会在自己的包名命名的栈中。而B C因为是singleTask模式,在启动B的时候就创建好了以B的TaskAffnity命名的栈中,创建C的时候,系统发现已经有了所需要的栈,因此现在栈中有BC。C再启动A,A是standard模式,系统会再次创建一个新的A对象,A进入启动它的C所在的栈中,其实栈里有BCA。再次A启动B,B有自己的栈,且根据singleTask启动模式,此时这个栈中有B对象,因此B对象上的所有对象都会弹出,B执行onNewIntent方法。此时这个栈中只剩下了B。按下back键后,B弹出,任务栈为空,栈被销毁,此时还剩下最初的A所在的栈,这时候回到后台任务栈将A显示出来,再back就回到了桌面。

通过这三个例子就能进一步理解activity的启动模式。

指定启动模式的两种方法

1. 在AndroidMenifest.xml中

<activity
    android:name = "com.lxy.test.MainActivity"
    android:configChanges="screenLayout"
    android:launchMode="singleTask"/>

2. 在Intent中设置标志位

Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

这两种方式的区别:

  1. 第二种的优先级高于第一种。两者同时存在时,以第二种为准。
  2. 限定范围上有所不同。第一种无法设置FLAG_ACTIVITY_CLEAR_TOP标志,第二种无法指定singleInstance模式。

Activity Flags

这里会展示一些比较常用的标记位

IntentFilter匹配规则

在书上看到这一部分才会想起来,原来IntentFilter是用在隐式调用的。刚开始学android的时候就学过这,不过后来因为每次启动一个活动的时候都用的是显示启动把这块就淡忘了。趁着这个机会再复习一下。

首先先向下隐式启动一般可以用在什么时候,它可以弥补显示启动的哪些。比如说我现在想调用百度的网页,就可以使用显示启动。通过intent的setData将百度网址传进去,调用系统浏览器就可以打开了。下面看看这个IntentFilter需要哪些规则。

intent-filter匹配规则

action匹配规则

action是一个字符串,系统预定义了一些action,同时也可以在应用中定义自己的action。

category匹配规则

data匹配规则

data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中也要定义可匹配的data。

data语法如下:

<data android:scheme="string"
    android:host="string"
    android:port="string"
    android:path="string"
    android:pathPattern="string"
    android:pathPrefix="string"
    android:mimeType="string"/>

data由两部分组成,mimeType和URI。

mimeType

媒体类型:image/jpeg audio/mpeg4-generic video/*等 可以表示图片、文本、视频等不同的媒体格式

URI

URI的结构:

<scheme:>//host:port/[<path>|<pathPrefix>|<pathPattern>]

实际的例子:

content://com.example.project:200/folder/subfolder/ect
http://www.baidu.com:80/search/info

每个数据的含义:

启动前判断

通过隐式Intent启动一个activity的时候,可以做下判断,看是否有activity能够匹配隐式intent。

返回最佳匹配的activity信息,否则返回null。

返回所有成功匹配的activity信息。

public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags)
public ComponentName resolveActivity(@NonNull PackageManager pm)
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)

lags一般填MATCH_DEFAULT_ONLY,表示只匹配intent-filter中声明default category的Activity。

一般在MainActivity注册的时候系统都会自动生成这个:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

他们的共同作用是表示这是一个activity的入口,并且会出现在activity的应用列表中,二者缺一不可。Service BroadCastReceiver,PackManager同样提供了类似的方法获取成功匹配的信息。

上一篇下一篇

猜你喜欢

热点阅读