进程的优先级
12.1.1.概述
Android规定:进程的优先级分为以下五个级别,如图-1所示:
图-1
1、 前台进程 -Activte process
Active (前台) process是包含(与用户交互的)控件的那种应用程序。这些是Android通过回收资源来极力保护的进程。Active process包括:
(1)处于“active”状态的Activity,它们运行在前台来响应用户的事件。
(2)Activity Service或者正在执行onReceive事件处理函数的Broadcast Receiver。
(3)正在执行onStart,onCreate,OnDestroy事件处理函数的Service。
2、 可见进程-Visible Process
可见但不活动的进程是那些拥有“可见”Activity的进程。“可见”Activity是那些在屏幕上可见,但不是在前台或不响应用户事件的Activity。这种情况发生在当一个Activity被部分遮盖的时候(被一个非全屏或者透明的Activity)。可见进程只在极端的情况下,才会被杀死来保证Active Process的运行。包括以下情况:
(1)可见的Activity,但处于暂停(onPause()) 状态;
(2)被可见Activity绑定的Service
3、 服务进程 Service process
进程中包含已经启动的Service。Service以动态的方式持续运行但没有可见的界面。因为Service不直接和用户交互,它们拥有比visible Process较低的优先级。它们还是可以被认为是前台进程,不会被杀死,直到资源被active/visible Process需求。
4、 背景进程 Background process
进程中的Activity不可见和进程中没有任何启动的Service,这些进程都可以看作是后台进程。在系统中,拥有大量的后台进程,并且Android按照后看见先杀掉的原则来杀掉后台进程以获取资源给前台进程。
5、 空进程-Empty process
为了改善整个系统的性能,Android经常在内存中保留那些已经走完生命周期的应用程序。Android维护这些缓存来改善应用程序重新启动的时间。这些进程在资源需要的时候常常被杀掉。
当一个进程被杀掉,进程保留,变成空进程。
12.1.2.设置/取消Service为前台进程的方法
由上所述,Service排在进程的第三优先级,通常耗时的操作是放在线程中,那么将这样的线程放在Service中将会有较高的优先级,降低被Android系统杀掉的几率。
若是将线程放在Activity中,当Activity被完全遮盖,处于onStop状态时,其进程的优先级别降为第四级。明显不如放在处于第三级别的Service中更保险。
应用场景,如音乐播放器,通过在前台做其它操作时,音乐播放器在后台播放音乐,这种情况将播放音乐的线程放在Service中是适宜的。
Service类中有两个方法,分别用来设置Service为前台进程和取消前台进程。被设置为前台进程的Service拥有最高的优先级别,被Android系统杀掉的几率降至最低。
1、startForeground(int id,Notification noti);
作用:设置Service对象为前台进程。
说明:
第一个参数是通知的id值。
第二个参数是通知对象。
startForegroud方法的参数与通知管理器相同,使用上也类似,都是发送一个通知,并指定该通知对象的id值。
2、stopForeGround(int id);
作用:取消(指定id值所通知的Service对象)前台进程。
12.1.3.设置Service为前台进程的步骤
步骤1、在Service类的onStartCommand方法中(通常在该方法中)创建Intent对象,并指定与其绑定的Activity,示例代码如下:
Intent foreIntent=new Intent(this, MainActivity.class);
步骤2、创建PendingIntetn对象
PendingIntent pintent=PendingIntent.getActivity(
this, 0, foreIntent, PendingIntent.FLAG_UPDATE_CURRENT);
说明:第四个参数指明在通知栏随时刷新通知。
步骤3、创建通知对象,示例代码如下:
Notification noti=new Notification(
R.drawable.icon,"notification",System.currentTimeMillis());
说明:
第一个参数是通知栏中显示的本通知的图标。
第二个参数是通知栏中显示的本通知的标题。
第三个参数是本通知发出的时间。
步骤4、将此通知放到通知栏的(Ongoing)正在运行组中,示例代码如下:
noti.flags=Notification.FLAG_ONGOING_EVENT;
步骤5、设置通知的点击事件,示例代码如下:
noti.setLatestEventInfo(this, "title","content", pintent);
步骤6、向指定的Activity发送通知,并设置当前的Service对象为前台进程,示例代码如下:
startForeground(97789, noti);
12.1.4.示例
运行图-1所示的窗口:
图-2
1、单击图-1中的start foreground按钮,将启动一个Service对象,并设置改Service为前台进程,在该在日志窗口中出现图-2中红框内的第一行信息。
2、单击图-2中的stop foreground按钮,将取消Service的当前进程,并在日志窗口中显示图-2中红框内的第二行信息。
以下列出关键代码:
步骤1、创建项目exer12_01,包名为com.tarena.exer12_01,项目入口:MainActivity类,该中关键代码如下所示:
@Override
public void onClick(View v) {
//创建Intent对象,并设置目标组件为MyService
Intent intent=new Intent();
intent.setClass(this, MyService.class);
switch(v.getId()){
case R.id.btnStartFore:
//设置intent.action的值为Constant.ACTION_FORE
intent.setAction(Constant.ACTION_FORE);
startService(intent);//启动服务
break;
case R.id.btnStopFore:
//设置intent.action的值为Constant.ACTION_STOP_FORE
intent.setAction(Constant.ACTION_STOP_FORE);
startService(intent);
break;
case R.id.btnStopService:
stopService(intent);//停止服务
break;
}
}
步骤2、在src/com.tarena.exer12_01包下创建MyService.java该类继承自Service类。关键代码如下所示:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action=intent.getAction();
if(Constant.ACTION_FORE.equals(action)){
Log.i(tag,"startForeground");
Intent foreIntent=new Intent();
foreIntent.setClass(this, MainActivity.class);
PendingIntent pintent=PendingIntent.getActivity(
this, 0, foreIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification noti=new Notification(
R.drawable.icon,"notification",System.currentTimeMillis());
//将此通知放到通知栏的"Ongoing"即"正在运行"组中
noti.flags=Notification.FLAG_ONGOING_EVENT;
noti.setLatestEventInfo(
this, "改变Service优先级", "设置service为foreground级别", pintent);
startForeground(97789, noti);
}else if(Constant.ACTION_STOP_FORE.equals(action)){
Log.i(tag,"stopForeground");
stopForeground(true);//取消当前服务为前台服务
}
return super.onStartCommand(intent, flags, startId);
}
步骤3、打开项目清单文件,注册该服务,如下代码中红框中代码所示:
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<service android:name="MyService"></service>
</application>
12.2.UI与线程
12.2.1.概述
UI是英文User Interface单词的简称。
当应用程序启动时,系统会为应用程序创建一个主线程(main)或者叫UI线程,它负责分发事件到不同的控件(例如绘画事件)以完成应用程序与Android UI孔庙件的交互。
例如,当触摸屏幕上的一个按钮时,UI线程会把触摸事件分发到控件上,更改状态并加入事件队列,UI线程会分发请求和通知到各个控件,完成相应的动作。
单线程模型的性能是非常差的,除非应用程序相当简单,特别是当所有的操作都在主线程中执行,比如访问网络或数据库之类的耗时操作将会导致用户界面锁定,所有的事件将不能分发,应用程序就像死了一样,更严重的是当超过5秒时,系统就会弹出“应用程序无响应”的对话框。
12.2.2.main线程
主线程也叫UI线程,主线程负责UI的创建,UI的刷新以及处理用户的输入事件。
提示:Android规定,Activity中的控件的刷新由主线程负责,其它线程不能直接刷新。
12.2.3.ANR术语
ANR的全称:Activity or Application is not responding,当用户操作超过系统规定的响应时间时,会弹出ANR对话框,如图-3所示:
图-3
若选择Force close按钮将强制关闭当前的Activity;
若选择Wait按钮将保留当前的Activity继续等待。
出现ANR的条件:
1. 在main线程(或称主线程)中有一个耗时操作正在执行,此时用户输入事件并且这个事件在5秒内没有得到响应,就会弹出ANR。
2. 广播接收者的onReceive()方法在10秒内没有执行完成,也会弹出ANR。
提示:在广播接收者的onReceive方法中要避免执行耗时的操作。
12.2.4.示例-测试ANR发生的两种情况
创建项目exer12_02,在该