Android组件---Activity
1.Activity定义、常见状态
Activity是用户和应用程序之间进行交互的接口,它提供了一个界面供用户进行各种点击、触摸、滑动等操作。
常见状态以及具体表现:
第一种:活动状态(runnning):
Activity处于活动状态时,用户可以进行点击、滑动等操作,屏幕会根据用户操作做出相应,Activity此时处于栈顶状态;
第二种:暂停状态(paused):
Activity处于暂停状态时,Activity暂时失去焦点,失去与用户交互的能力,成员变量和状态信息还存在;
第三种:停止状态(stopped):
Activity处于停止状态时,Activity被其他Activity完全覆盖,成员变量和状态是否被回收取决于内存是否紧张;
第四种:被回收状态(killed):
Activity处于被回收的状态时,Activity所保存的成员变量和状态信息都会被回收。
2.生命周期
onCreate()-->onStart()-->onResume()-->onPause()-->onStop()-->onDestory()-->onRestart() ;
创建-->开始-->运行-->暂停-->停止-->销毁-->重启
onCreate : Activity第一次创建时会调用此方法;
onStart : Activity启动时(不可见变为可见)时调用;
onResume : Activity运行时(活动准备好和用户交互/恢复一个活动)调用,运行时位于返回栈栈顶;
onPause:Activity暂停时(准备启动一个活动或恢复另一个活动)调用;
onStop : Activity状态变为完全不可见时调用;
onDestory : Activity被销毁之前调用;
onRestart : Activity重启时(Activity由停止状态变为运行状态时)调用;
注意:
大部分情况下:Activity生命周期的方法是成对出现的:
onStart与onStop:从activity是否完全可见的角度
onResume与onPause:从activity是否位于前台(UI最顶层)的角度(上述两对,除了回调时刻,实际中无任何区别)
完整生存期:onCreate~onDestory
可见生存期:onStart~onStop
前台生存期:onResume~onPause
代码实践
1.A活动--->B活动
(A)onPause--->(B)onCreate--->(B)onStart--->(B)onResume--->(A)onStop
2.B活动--->A活动
(B)onPause--->(A)onCreate--->(A)onStart--->(A)onResume--->(B)onStop
3.A活动--->B活动--->Back键回退到A活动
(B)onPause--->(A)onRestart--->(A)onStart--->(A)onResume--->(B)onStop--->(B)onDestory
4.A活动--->Home键--->点击应用回到A活动
(A)onCreate--->(A)onStart--->(A)onResume--->Home键
(A)onPause--->(A)onStop--->应用图标--->
(A)onRestart--->(A)onStart--->(A)onResume
3.启动模式
// 任务栈
1.Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动集合,这个栈也称为返回栈;
2.栈的原则:先进后出,系统总是会显示处于栈顶的活动给用户;
3.每当我们启动了一个新的活动,它都会返回栈中入栈,并处于栈顶的位置,每当我们按下返回键或者调用finish()方法销
4.毁一个活动时,处于栈顶扥活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。
注:可以使用 adb shell dumpsys Activity 查看Activity栈信息,来分析Activity启动时栈的情况
// 启动模式种类
1.标准模式(standard):每次启动一个Activity都会创建一个新的实例,每创建一个Activity都会走相应的生命周期;
2.栈顶复用模式(singleTop):如果创建的Activity处于栈顶,就不会创建新的实例,会复用栈顶的Activity;
3.栈内复用模式(singleTask):此模式会检测整个任务栈是否存在当前所需要启动的Activity,存在直接置于栈顶,
并将这个Activity以上的所有Activity全部删除(销毁),但需要注意,此时会在Activity中回调一个onNewIntent()方法;
4.单实例模式(singleInstance): 此模式下,设置次此模式的Activity在整个系统中有且只有一个实例,且这个Activity独享任务栈;
// 通过Intent设置标志位:
FLAG_ACTIVITY_SINGLE_TOP:指定启动模式为栈顶复用模式
FLAG_ACTIVITY_NEW_TASK:指定启动模式为栈内复用模式
FLAG_ACTIVITY_CLEAR_TOP:所有位于其上层的Activity都要移除,SingleTask模式默认具有此标记效果
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有该标记的Activity不会出现在历史Activity的列表中,即无法通过历史列表回到该Activity上
例如:
Intent inten = new Intent (ActivityA.this,ActivityB.class);
intent,addFlags(Intent,FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// 设置方式:
1.AndroidManifest中设置
2.代码中使用Intent方式设置
两种设置方式的区别:
1.优先级不同:代码中使用Intent优先级>AndroidManifest设置
2.限定范围不同:AndroidManifest无法设置FLAG_ACTIVITY_CLEAR_TOP;Intent设置方式无法设置单例模式(SingleInstance)
注意:启动模式
startActivityForResult方法能够起效:standard和singleTop
startActivityForResult方法不能够起效:singleTask和singleInstance
1)只要将被启动的Activity属性设置为singleTask则一定不起效
2)只要将被启动的Activity属性设置为singleInstance则一定不起效
3)只要将启动的Activity模式设置为singleInstance则不论被启动的Activity为什么模式,均不起效.
4.启动方式
// 优秀文章:https://carsonho.blog.csdn.net/article/details/82848840
1.显式Intent启动:
第一种:使用构造函数传入Class对象
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
第二种:使用setClassName传入(包名+类名) 或者 (上下文+类的全限定名称)
Intent intent = new Intent();
intent.setClassName("com.xyl.demo","com.xyl.demo.FirstActivity");
或者
intent.setClassName(context,"com.xyl.demo.FirstActivity");
startActivity(intent);
第三种:通过ComponentName传入 包名+类名
Intent intent = new Intent();
ComponentName cn = new ComponentName("com.xyl.demo","com.xyl.demo.FirstActivity");
intent.setComponent(cn);
startActivity(intent);
2.隐式Intent启动(通过Category、Action设置)
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory("com.hc.second");
intent.setAction("com.hc.action");
startActivity(intent);
参数解析:
Action(动作):指明要实施的动作,不设置action则认定匹配失败
Category(类型):
指定处理该intent的组件的种类信息,每个intent可以设置多个category,没有设置则默认匹配成功
Data(数据):
指明媒体类型(mimeType),URI地址,data需要和<intent-filter>其中一个匹配(不设置则默认匹配失败)
匹配规则:
只有Action、Category、Data同时匹配时,才能成功启动Activity
例子:
1.打开网页
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.parse("http://www.baidu.com");
intent.setData(uri);
startActivity(intent);
2.打开相册、图库
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivity(intent);
3.发送短信
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT,"It is a messsage");
startActivity(intent);
4.打开电话页面
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.parse("tel:123456");
intent.setData(uri);
startActivity(intent);
5.启动过程
// 优秀文章:https://carsonho.blog.csdn.net/article/details/82848840
1.Launch进程通过Binder驱动向ActivityManagerService类发起startActivity请求;
2.ActivityManagerService类接收到请求后,向ActivityStack类发送启动Activity的请求;
3.ActivityStack类记录需启动的Activity信息并调整Activity栈将其置于栈顶、通过Binder驱动
将Activity的启动信息传递到ApplicationThread线程中(即Binder线程)
4.ApplicationThread线程通过Handler将Activity的启动信息发送到主线程ActivityThread
5.主线程ActivityThread类接收到该信息或者请求后,通过ClassLoader机制加载对应的Activity类,
最终调用Activity的onCreate方法,最后启动完毕
6.启动Activity卡顿的原因
1.内存泄漏:
解析:占用内存过高,集合类,Static关键字修饰的成员变量,非静态内部类/匿名类,资源对象使用后未关闭
解决方案:直接查看解决内存泄漏的文章
2.加载大数据(图片资源Bitmap):
解析:占用内存过多,导致jvm频繁的GC
解决方案:
压缩图片:根据所需显示Bitmap宽高,设定采样率
缓存图片:采用LRUCache方法
其它大数据(3D模型),则可使用Native空间
3.UI线程做耗时操作(数据库操作,数据计算)
解析:不卡顿频率,每秒60帧,即16.6ms刷新一次
解决方案:另起工作线程执行耗时任务
4.UI视图过度绘制
解析:绘制时间过长
解决方案:减短UI视图的绘制时间
7.加速启动的方式
1.减少onCreate方法中的代码逻辑,对于只需要初始化1次的方法,尽量不要放在onResume方法里边
上述问题解决方案:
定义boolean变量,在onCreate中标记为true,在onResume中判断为true则进行初始化;
初始化完毕,则将变量置为false.
2.优化布局文件,减少布局嵌套层级,使用include
3.提高Adapter的效率,数据过多也会导致绘制效率低,导致卡顿
解决方案:
重用已生成的item view
添加viewHolder
缓存item的数据
分页显示
4.减小主线程的阻塞时间
导致ANR的原因:用户无响应5s、网络数据库阻塞10s、广播接收者执行超过10s
解决方案:
将耗时过长的操作放入后台线程中执行
只在需修改UI时通知主线程修改
8.状态保存
要点:
Activity异常 : onSaveInstanceState() 用来保存数据
Activity重新创建后进行数据恢复: onRestoreInstanceState()用来恢复数据
背景:有a,b两个Activity,当a进入b之后,系统可能会把a回收
冲突:此时点击back键,会重新创建a,而不是onRestart,这时,a中的临时数据和状态可能会丢失
解决方案:
1.onSaveInstanceState--->用于保存临时数据&状态
2.当某个Activity即将被系统摧毁时,该Activity会执行onSaveInstanceState
注意:若是被用户主动销毁(如用户按Back键),则不会被调用
状态保存的两个常用方法:
onSaveInstanceState与onRestoreInstanceState在用户主动调用back键销毁Activity时,不会被调用;
1.onSaveInstanceState:保存Activity的数据/状态
调用时机:当系统"未经用户许可",可能销毁了活动,onSaveInstanceState会被调用
调用场景:
1)从活动A启动新Activity
2)屏幕方向切换时
3)用户按下Home键
4)关闭屏幕显示
由于onSaveInstanceState调用的不确定性,使用onPause方法存储持久化数据
2.onRestoreInstanceState:恢复Activity的临时数据/状态
调用时机:当系统"未经用户许可",确定销毁了活动,onRestoreInstanceState会被调用
调用场景:
1)异常关闭了Activity,即调用onSaveInstanceState,则下次启动时会调用onRestoreInstanceState
此时Activity的生命周期为:onCreate-->onStart-->onRestoreInstanceState-->onResume
注意:onSaveInstanceState与onRestoreIntanceState两个方法不一定成对出现;
onSaveInstanceState的bundle参数会传递到onCreate方法中,可选择在onCreate方法中进行数据还原;
9.Activity与组件之间的通信
总结:
Activity--->Activity:intent
Activity--->Fragment:通过Bundle + Argument方式
Fragment--->Activity:通过接口回调的方式
Activity--->Service:绑定服务或intent
1.通过intent进行传递,具体使用Bundle对象对传递的参数进行存取
2.Activity传递数据到Fragment
方式一:通过bundle方式传递,
在Activity创建Bundle对象放入key,value,然后初始化目标fragment,调用setArguments方法
放入bundle对象到 Fragment中,例如onStart方法中先判断当前Fragment
是否依附于Activity,如果是则通过getArguments方法根据key获取传递的值;
示例如下:
Activity中--传达
Bundle bundle = new Bundle();
bundle.putString("message","I love me");
fragment.setArguments(bundle);
fragmentTransaction.add(R.id.fragment_container,fragment).commit();
Fragment中--接收
Bundle bundle = getArguments();
if(bundle!=null){
String message = bundle.getString("message");
}
方式二:
直接在Activity中定义方法,然后在Fragment获取对应Activity并进行强转,
然后调用方法获取传递的数据
3.Fragment传递数据到Activity
采用接口回调的方式,示例如下:
例如,此时要将Fragment中的一个String字符串传递到Activity中
1)定义一个接口,例如
public interface ICallBack {
void getMessage(String message);
}
2)在Fragment中声明一个可供外部访问的方法,例如
String message = "momoda";
public void sendMessage(ICallBack callBack){
callBack.getMessage(message);
}
3)在Activity中通过Fragment的实例获取,例如:
FirstFragment fragment = new FirstFragment();
点击事件触发获取Fragment中的文本内容
fragment.sendMessage(new ICallBack(){
@Override
public void getMessage(String message){
text.setText(message);
}
});
4.Activity传递数据给Service
方式一:绑定服务
让Activity实现ServiceConnection接口,然后重写对应的两个方法;
分别为:onServiceConnected与onServiceDisconnected,前者为绑定成功时调用,
后者为绑定失败时调用在Activity中定义 目标Service.Binder 的binder对象,在
onServiceConnected方法中获取目标Service中的Binder对象;然后在启动Service的时候,使用setData传递数据
方式二:intent传值
在Activity中,进行startService时,使用intent.putExtra(key,value),
startService(intent)传递数据;然后在Service的onStartCommand中接收
从Activity中传递过来的值,直接调用intent.getStringExtra(key);
优点:代码简单
缺点:只能传递一些简单的数据,性能上没有太多优势
方式三:使用callBack+handler
10.横竖屏切换
实现横竖屏切换有两种方式:
方式一:
在需要横屏的Activity中的onCreate方法中添加以下语句,并要求该语句位于onCreate方法内setContentView语句之前;
setRequestedOrientation(ActivityInfo.SCREEN.ORIENTATION_FULL_SENSOR);
在该方式下进行横竖屏切换,对应的Activity数据会丢失,可以在对应的Activity重写这两个方法,来保证数据不丢失;
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//横竖屏切换前调用,保存用户想要保存的数据,以下是样例
outState.putString("name","yoosir");
outState.putInt("age",24);
outState.putBoolean("handsome",true);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 屏幕切换完毕后调用用户存储的数据,以下为样例:
if(savedInstanceState != null) {
int age = savedInstanceState.getInt("age");
String name = savedInstanceState.getString("name");
boolean isHandsome = savedInstanceState.getBoolean("handsome");
}
}
方式二:
代码中定义,这种方式在横竖屏切换时,执行的操作是:
销毁当前Activity--根据新的屏幕尺寸重建Activity,如果不进行数据存储的操作,
再切换的过程中Activity中的数据会丢失
这种方式的关键点在AndroidManifest中声明Activity属性--configChanged,例如:
android:configChanges="orientation|keyboardHidden|screenSize"
配置configChanges以上属性后,切换屏幕不会重新调用各个生命周期,只会执行
onConfigurationChanged方法,并且Activity的数据不会被销毁;
反之,如果不配置或者非以上配置时,切屏会重新调用Activity的各个生命周期,数据也会被销毁。
示例代码:
<activity android:name=".com.cdsn.SearchActivity"
android:screenOrientation="sensor"
android:configChanges="keyboardHidden|orientation|screenSize"/>
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 在这里添加屏幕切换后的操作
}
注意:网友遇到一个问题
描述:
根据重力传感器实现了屏幕旋转,可以做到屏幕随重力传感器切换,但是发现无论手机设置中"屏幕旋转"或"方向锁定"是开是关,
App内的Activity都会随着手机的横竖方向切换横竖屏。
网友想达到的效果:
当打开"屏幕旋转"时,APP内的Activity跟随重力感应器,当关闭"屏幕旋转"时,APP内的Activity固定为默认方向,如何做到
关闭重力传感器时,APP也关闭屏幕自动旋转?
解决方案:AndroidManifest定义属性
android:screenOrientation="sensor"修改为android:screenOrientation="user"
处理方案解析:
当参数为sensor时,无论是否关闭"屏幕设置",App内的特定Activity都会根据重力传感器改变横竖屏。
当参数为user时,当"屏幕旋转"开启时,则特定Activity根据重力传感器改变横竖屏;
当"屏幕旋转"关闭时,则特定Activity会固定默认方向(一般为默认正面竖屏)。
ActivityInfo有关的屏幕旋转参数
| 屏幕旋转参数 | 功能 |
|---|---|
| SCREEN_ORIENTATION_BEHIND | 继承Activity堆栈中当前Activity下面的那个Activity的方向 |
| SCREEN_ORIENTATION_FULL_SENSOR | 由重力setRequestedOrientation传感器决定0/90/180/270° |
| SCREEN_ORIENTATION_LANDSCAPE | 始终横屏 |
| SCREEN_ORIENTATION_PORTRAIT | 始终竖屏 |
| SCREEN_ORIENTATION_LOCKED | 锁定屏幕方向 |
| SCREEN_ORIENTATION_NOSENSOR | 关闭重力传感器对横/竖屏的影响 |
| SCREEN_ORIENTATION_REVERSE_LANDSCAPE | 另一个方向的横屏 |
| SCREEN_ORIENTATION_REVERSE_PORTRAIT | 另一个方向的竖屏(倒拿手机) |
| SCREEN_ORIENTATION_SENSOR | 重力传感器影响屏幕的方向0/90/270° |
| SCREEN_ORIENTATION_SENSOR_LANDSCAPE | 始终横屏,由重力传感器决定是哪个方向的横屏 |
| SCREEN_ORIENTATION_SENSOR_PORTRAIT | 始终竖屏,由重力传感器决定是哪个方向的竖屏 |
| SCREEN_ORIENTATION_UNSPECIFIED | 不指定方向,使用默认方向 |
| SCREEN_ORIENTATION_USER | 由用户和重力传感器共同决定,详见文本末端 |
| SCREEN_ORIENTATION_USER_LANDSCAPE | 用户和重力传感器共同决定是哪个方向的横屏 |
| SCREEN_ORIENTATION_USER_PORTRAIT | 用户和重力传感器共同决定是哪个方向的竖屏 |
| UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW | 当屏幕较窄时导航栏有一部分会显示在底部 |
android:screenOrientation属性可设置的参数数如下:
| 值 | 描述 |
|---|---|
| unspecified | 默认值。系统自动选择屏幕方向 |
| behind | 跟activity堆栈中的下面一个activity的方向一致 |
| landscape | 横屏方向,显示的宽比高长 |
| portrait | 竖屏方向,显示的高比宽长 |
| sensor | 由设备的物理方向传感器决定,如果用户旋转设备,这屏幕就会横竖屏切换 |
| nosensor | 忽略物理方向传感器,这样就不会随着用户旋转设备而横竖屏切换了(”unspecified”设置除外) |
| user | 用户当前首选的方向 |
| reverseLandscape | API 9 以上,反向横屏 |
| reversePortrait | API 9 以上,反向竖屏 |
| sensorLandscape | API 9 以上,横屏,但是可以根据 物理方向传感器来切换正反向横屏 |
| sensorPortrait | API 9 以上,竖屏,但是可以根据 物理方向传感器来切换正反向竖屏 |
| fullSensor | API 9 以上,上下左右四个方向,由物理方向传感器决定 |
| locked | API 18 以上,锁死当前屏幕的方向 |
android:configChanges属性可设置的参数如下:
| 值 | 描述 |
|---|---|
| mcc | IMSI移动台的国家代码(MCC)发生变化——一个SIM被探测到并且更新MCC |
| mnc | IMSI移动台的网络代码(MNC)发生变化——一个SIM被探测到并且更新MNC |
| locale | 区域发生变化——用户选择了一个文本需要显示的新语言 |
| keyboard | 键盘类型发生变化——例如:用户插入了外接键盘。 |
| keyboardHidden | 键盘的可访问性发生变化——例如:用户发现了硬件键盘。 |
| screenLayout | 屏幕布局发生变化——这个会导致显示不同的Activity。 |
| 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里加入的。 |
| layoutDirection | 布局方向变化。例如书写方式从左向右(LTR)转换为从右向左(RTL) |
11.Scheme跳转协议
// 什么是Scheme协议?
Android的Scheme是一种页面内跳转的协议;
// 应用场景
1.cs交互,服务器下发跳转路径,客户端根据服务端的参数跳转到相应的界面
2.js交互,H5页面点击锚点,客户端根据锚点跳转到相应界面
3.通知栏跳转,客户端根据通知栏的消息定制化跳转到界面
4.不同应用之间跳转,APP根据URL跳转到另一个APP的相应页面
// 协议格式
scheme协议分为:scheme,host,port,query,path
例如示例路径:zr1://test:8000/zr?id=9&name=hello
具体路径分析如下:
scheme:zr1
host:test
port:8000
path:zr
query:id=9&name=helllo
Android中scheme协议的应用:
intent.setData(Uri.parse("http://www.baidu.com"))
解析:
android:scheme-->用于指定数据的协议部分,如http
android:host-->用于指定数据的主机名部分,如www.baidu.com
android:port-->用于指定数据的端口部分,一般紧随在主机名之后
android:path-->用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
android:mimeType-->用于指定可以处理的数据类型,允许使用通配符的方式进行指定
关键点:只有<data>标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能够响应该Intent.
不过一般在<data>标签中都不会指定过多的内容,如上面浏览器示例中,其实只需要指定
android:scheme 为 http,就可以响应所有的 http 协议的 Intent 了.
12.进程的优先级
// 关于进程须知
首先,Android的系统资源有限,不可能对所有进程都保持宽容不杀死;
当资源不足时,系统会根据策略,选择性的杀死部12.进程的优先级分进程。
这个策略就是对所有进程标记优先级,优先级低的先杀死。
// 进程优先级分类
Android进程优先级分类,共有5个层次:
1.前台进程(Foreground process):
1)Activity生命周期处于onResume状态的进程
2)正在与Activity/Service交互的进程
3)正在执行生命周期方法的进程
4)正在执行onReceive的广播进程
注意:一般不会有太多前台进程,杀死前台进程是系统无奈的做法,但是当内存严重不足的时候,前台进程一样会被杀掉。
2.可见进程(Visible process):
特点:表明此进程没有持有任何前台组件,但是它还是影响用户看得到的界面。
1)该进程持有一个非前台Activity,但这个Activity还可以被用户看到(也就是这个Activity被调用了onPause方法)
2)该进程持有一个与可见Activity绑定的Service.
3.服务进程(Service process):
除了符合前台进程和可见进程的Service,其它Service都被归类为服务进程。
4.后台进程
持有不可见Activity(调用onStop方法)的进程即为后台进程。
通常境况下都会有很多后台进程,当内存不足时,在所有的后台进程里面,会按照LRU(最近使用)原则,
优先回收最长时间没有使用过的进程。
5.空进程(Empty process):
不持有任何组件的进程。
保持这种进程只有一个目的。就是为了缓存,以便下一次启动该进程中的组件时能够更快相应。
当资源紧张时,系统会平衡进程缓存和底层的内核缓存情况进行回收。
// 额外须知:
第一点:
如果一个进程同时满足上述5中优先级中的多个等级条件,Android系统会优先选取其中最高的等级作为该进程的优先级。
例如:一个进程持有Service(服务进程等级)和一个前台Activity(前台进程等级),那么操作系统会将其标记为前台进程。
第二点:
如果一个进程为另外一个进程提供服务,那么这个进程的优先级不会低于享受服务的进程。
例如:假设进程A中的Content Provider为进程B提供服务,或者进程A中有一个Service与进程B中的组件进程绑定,
那么进程A的优先级至少要与进程B一致,或者高于进程B。