Android组件---Activity

190 阅读19分钟

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/828488401.Launch进程通过Binder驱动向ActivityManagerService类发起startActivity请求;
    
2.ActivityManagerService类接收到请求后,向ActivityStack类发送启动Activity的请求;
    
3.ActivityStack类记录需启动的Activity信息并调整Activity栈将其置于栈顶、通过Binder驱动
  将Activity的启动信息传递到ApplicationThread线程中(即Binder线程)        
    
4.ApplicationThread线程通过HandlerActivity的启动信息发送到主线程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键),则不会被调用
    
    
状态保存的两个常用方法:
onSaveInstanceStateonRestoreInstanceState在用户主动调用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         
    
    注意:onSaveInstanceStateonRestoreIntanceState两个方法不一定成对出现;
    onSaveInstanceStatebundle参数会传递到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用户当前首选的方向
reverseLandscapeAPI 9 以上,反向横屏
reversePortraitAPI 9 以上,反向竖屏
sensorLandscapeAPI 9 以上,横屏,但是可以根据 物理方向传感器来切换正反向横屏
sensorPortraitAPI 9 以上,竖屏,但是可以根据 物理方向传感器来切换正反向竖屏
fullSensorAPI 9 以上,上下左右四个方向,由物理方向传感器决定
lockedAPI 18 以上,锁死当前屏幕的方向

android:configChanges属性可设置的参数如下:

描述
mccIMSI移动台的国家代码(MCC)发生变化——一个SIM被探测到并且更新MCC
mncIMSI移动台的网络代码(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):
    1Activity生命周期处于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