在前面的博客当中为大家分析过了Handler的源码,今天这篇博客的主要内容是为大家结合HandlerThread讲述一下Handler的另外一种比较酷炫的用法,以及这种酷炫的用法又是如何结合Service创造出IntentService这么酷的类的。
首先为大家总结一下前面Handler几个关键的核心知识点:
1、Handler是通过Handler的实例化对象进行线程间的切换的
2、Handler必须在主线程中创建,因为不在主线程中创建的Handler没有Loop,在Handler源码中设置就会报错。没有Loop就无法进行Handler最主要的消息机制
3、Handler消息的解释是通过Loop.loop()方法中通过Handler的实例化对象(msg.target)回调去实现Runnable接口中的内容
4、不同的线程都有不同的Looper和相应的消息队列(MessageQueue),都存储在一个叫ThreadLocal的类当中
这些特性就解释了Handler应用的一些表面现象:
1、即使在不同的线程中只要是同一个Handler对象都能接收到同一个Handler对象所发的消息,因为消息的回调是通过Handler的实例化对象
2、即使在不同的界面,同样也能接收到消息,只要在同一个线程中就可以了,因为同一个线程用的是同一个消息队列所以能够收到消息。
复习完了Handler的知识点,下面给大家看一段酷炫的Handler的用法,代码如下:
HandlerThread handlerThread = new HandlerThread("HandlerThread");
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("threadIdRunnable = " + thread.getId());
}
});
}
这里给大家的思考就是Handler中的Runnbale接口执行的时候,里面的代码是在主线程执行的么?
答案当然是否定的,这段代码的执行时在HandlerThread子线程当中执行的,利用了消息队列的特性,即使Handler.post(new Runable())方法使用很多次,使消息队列中有很多消息,也不会产生线程不安全的问题,因为这是个消息队列,只会等一个消息执行完了再执行下一个消息,Handler的这种用法即避免了在主线程执行耗时操作,又避免了线程不安全的问题,是不是很酷。
那么就大家来分析一下能够产生这种效果的原因:
大家在前面应该看到了HandlerThread这个类,先给大家看一下HandlerThread这个类的源代码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
大家仔细看HandlerThread 这个类继承自Thread,和普通Thread唯一不同的地方就是在于这个类用于自己的Loop.
这下明白了,如果一个Handler使用了HandlerThread中的Loop,那么在当执行Hanndler中Runnable接口中的内容的时候。必须先执行Loop.loop()这个方法才能在当中利用Handler的实例化对象回调Runable接口中的内容。但是Loop.loop()这个方法由于Loop是HandlerThread的,所以Loop.loop()这个方法的调用必须是在HandlerThread中,这就解释了为什么说这个操作是在子线程当中。OK,为大家讲述完了Hanlder结合HandlerThread的酷炫用法.
下面为大家讲述IntentService,IntentService实际上是一个继承自Service的类,首先为大家先来复习一下关于Service的生命周期。主要讲述常用的几个生命周期,用StartService的方式启动服务
onCreate() 第一次启动服务的时候会回调这个方法
onStartCommand() 后面每次启动都会回调这个方法
onDestroy() 服务销毁的时候会启动这个方法
那么大家来看看在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);
}
小伙伴们是不是惊呆了,没错,这个其实就是我上面为大家讲述的关于Handler的炫酷用法,在一个Handler了里面指定了一个HandlerThread的Loop,这样后面Hanlder方法里面的操作就在子线程里面了,还不会出现因为多线程带来的线程不安全的问题,因为Handler的处理机制是一个消息队列。
那在Service的生命周期onStartCommond里面做了什么呢,
大家情况源码:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
回调了onStart()里面的内容,情况onStart()里面的代码:
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
onStart里面的操作竟然就是把intent和startId当做消息用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,其实就是接收了消息,然后调用了onHandleIntent()这个方法,所以这也是为何我们使用IntentService的时候要重写这个方法,IntentServcie的精华就在于利用了Service这个组件在后台运行的特性,并且利用了Handler使用子线程的Looper的话,那么Handler里面的操作就是在子线程里面,并且利用Handler的消息队列机制避免了线程的不安全性。接着调用了stopSelf()方法把自己这个服务干掉了,并且看onDestroy()方法:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
将这个消息队列的停止了,大家在这个过程中注意这一点避免内存泄露。