一、IntentService的使用
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super(TAG);
println("MyIntentService()");
}
@Override
public void onCreate() {
super.onCreate();
println("onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
println("onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
println("onDestroy()");
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
println("onBind()");
return super.onBind(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
println("onHandleIntent()");
try {
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}
}
private void println(String info) {
System.out.println(TAG + " " + info);
}
}
实际上,如果需要使用IntentService,直接重写onHandleIntent(Intent intent)这个方法就行。不过在这里,主要是为了看看IntentService的生命周期,所以把IntentService的所有方法都重写了一遍,然后将相关的日志打印出来。
在onHandleIntent(Intent intent)里,就只打印了一行日志,然后将当前线程暂停6秒。为什么是6秒,因为在主线程中,如果暂停时间超过5秒,那么就会产生臭名昭著的ANR问题。在这里主要就是想看看,如果在这里线程暂停超过5秒,会不会产生ANR。
<service android:name=".service.MyIntentService"/>
首先在清单文件中将这个服务进行注册,然后通过一个按钮来启动服务。
startService(new Intent(this, MyIntentService.class));
启动服务之后,等待一段时间查看日志,日志如下
I/System.out: MyIntentService MyIntentService()
I/System.out: MyIntentService onCreate()
I/System.out: MyIntentService onStartCommand()
I/System.out: MyIntentService onHandleIntent()
I/System.out: MyIntentService onDestroy()
通过上述的日志可以看出,这个服务会自动停止,并且不会导致ANR问题。那么下面,我们将通过源码看看,为什么IntentService会自动停止,并且不会导致ANR问题。(在普通的Service中执行上述的Thread.Sleep(6000)将会导致ANR)。
二、IntentService代码解析
在这里我们就通过服务的回调函数执行顺利来分析。首先是onCreate()
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
在这里主要是启动一个HanderThread,这里我们称之为服务线程,然后给这个线程定义一个Handler(注意,这里不是给UI线程创建的Handler)。然后再看看onStartCommand()的实现。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
这里很简单,先调用onStart(intent, startId),然后给一个返回值。那么我们直接去看onStart()里的实现好了。
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
先通过这个线程的Handler去获取一个消息,然后将startId,intent这些参数赋值给这个消息。通过服务线程的Handler将这个消息发送到这个线程的消息队列中去,然后这个线程中的Loop方法就会解除阻塞,获取到这个消息,并将这个消息交给这个消息指定的Handler进行处理。那么我们来看看这个handler的实现。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
在handleMessage(Message msg)实现比较简单,先是将启动这个服务的Intent从消息中取出,然后调用onHandleIntent((Intent)msg.obj)函数。onHandleIntent函数的定义在上面已经看过,很简单,就是打印一条日志,然后暂停6秒钟。onHandleIntent函数完成后,就会调用stopSelf(msg.arg1)来停止这个服务。
现在我们来梳理一下:
- 通过在onCreate()中启动一个HanlderThread线程(考虑一下,为什么不在onStartCommand中启动这个线程)。
- 在onStartCommand中获取startId和Intent,然后通过Handler获取消息,将startId和Intent设置在这个消息中,然后通过Handler将这个消息发送到消息队列中。
- 在loop()函数中,由于消息的入队,这个时候会解除阻塞,接收这个消息,然后将这个消息直接给这个Handler处理。
- 因为Handler是在服务线程中运行的,所以虽然在onHandleIntent中等待了6秒钟,但是由于不是在UI线程,所以不会导致ANR。
- 在执行完onHandleIntent后,IntentService会自动调用stopSlef来停止服务本身。