在Android开发中,我们知道可以在主线程中直接使用Handler,这是因为在APP的入口,系统就已经调用Looper.prepareMainLooper(),和Looper.loop(),但是如果我们想在子线程使用handler,也很简单,只要先使用Looper.prepare();然后调用Looper.loop()后,就可使用handler了,但是可能有些小伙伴觉得也麻烦,是不是应该有一种对这种进行封装的框架呢?没错,系统里有个HandlerThread就是干这件事的。
我们来看看HandlerThread的使用:
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
Looper looper = thread.getLooper();
MyHandler myHandler = new MyHandler(this,looper);
首先,创建一个HandlerThread对象,然后必须先调用start()方法,因为HandlerThread是继承Thread的,调用start()开启线程的执行,可以看一下其run()方法的源码:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
可以看到,run方法里面做了对Looper的初始化操作,初始化完成后回调了onLooperPrepared()方法,当我们需要做一些在Looper初始化完成并在开启轮询之前的操作的时候,我们就可以继承该类然后复写自己的onLooperPrepared方法做我们需要的工作,这样设计增加了其扩展性。
下面我们回到主线程的使用当中,接着调用thread.getLooper(),看一下该方法源码:
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;
}
首先,判断Looper的线程是否存活的,然后进入同步代码块中,此代码块是个while循环,如果线程是存活的并且mLoope这时还没有被初始化好,这时该线程(主线程)就会等待,等到mLoope初始化好之后,子线程会调用notifyAll()来唤醒,然后返回mLooper。
接下来我们就可以使用返回的Looper创建Handler了:
MyHandler myHandler = new MyHandler(this,looper);
这样通过此handler发送的消息都会被创建此Looper的子线程拿去处理了。
我们再来看一下HandlerThread的一个典型的应用:系统为我们提供的IntentService ,先贴出其源码:
package android.app;
import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
/**
* IntentService is a base class for {@link Service}s that handle asynchronous
* requests (expressed as {@link Intent}s) on demand. Clients send requests
* through {@link android.content.Context#startService(Intent)} calls; the
* service is started as needed, handles each Intent in turn using a worker
* thread, and stops itself when it runs out of work.
*/
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
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);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
先介绍一下IntentService,这是一个Service的子类,专门用来在子线程处理异步任务的,处理完成之后会自动的结束服务。
我们可以看到,在onCreate()方法中使用了HandlerThread,和我们之前那段代码几乎差不多,在onStart方法中通过Handler将intent信息发送到消息队列中,然后在子线程中调用handleMessage方法去处理 ,这里可以看到handleMessage方法中,先调用了onHandleIntent((Intent)msg.obj),这是个抽象方法,需要我们自己去实现,我们可以在其中做一些耗时的任务,任务完成之后会自动调用stopSelf(msg.arg1)结束服务。在onDestroy中调用mServiceLooper.quit()结束消息队列,防止此Service出现内存泄漏。当我们多次调用startService开启服务时,会走onStartCommand方法,从源码中看到,它也会调用onStart(intent, startId)方法,然后将intent信息发送过去,由此,我们能得出,当有多个任务时,这些任务都被依次的存入子线程的消息队列中,待子线程一个一个取出去处理,如果前面的任务耗时,则会阻塞后面的任务。
这里有一点需要了解一下,前面讲了在任务处理完成之后会调用stopSelf(msg.arg1)结束服务,那么你也许会问:如果消息队列中有多个任务,既然第一个任务完成之后就把服务给结束了,那之后的任务怎么办?
这里我们就需要来看看stopSelf(msg.arg1)的源码了:
/**
* Old version of {@link #stopSelfResult} that doesn't return a result.
*
* @see #stopSelfResult
*/
public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}
可以看到是通过AIDL调用ActivityManagerService来停止服务的,关于怎么关闭的,这里就不介绍了,有兴趣的可以去看看源码,这里我们看其官方注释,提示此方法是stopSelfResult的老版本,我们来看看stopSelfResult的源码:
/**
* Stop the service if the most recent time it was started was
* <var>startId</var>. This is the same as calling {@link
* android.content.Context#stopService} for this particular service but allows you to
* safely avoid stopping if there is a start request from a client that you
* haven't yet seen in {@link #onStart}.
*
* <p><em>Be careful about ordering of your calls to this function.</em>.
* If you call this function with the most-recently received ID before
* you have called it for previously received IDs, the service will be
* immediately stopped anyway. If you may end up processing IDs out
* of order (such as by dispatching them on separate threads), then you
* are responsible for stopping them in the same order you received them.</p>
*
* @param startId The most recent start identifier received in {@link
* #onStart}.
* @return Returns true if the startId matches the last start request
* and the service will be stopped, else false.
*
* @see #stopSelf()
*/
public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) {
return false;
}
try {
return mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false;
}
代码和stopSelf几乎差不多,就是返回值从void改变为boolean,意味着这个并方法不一定能真正的停掉服务,我们再来看看方法参数和返回值的注释:
@param startId The most recent start identifier received in {@link #onStart}.
@return Returns true if the startId matches the last start request and the service will be stopped, else false.
startId是通过onStart方法接收的ID。
如果需要关闭的startId和最后一次请求服务的时候的startId相匹配,就停止服务,如果不匹配,则不停止。什么意思?就是说如果你多次调用startService启动了服务,此时最后“最后一次请求服务的时候的startId”就是你最后请求的那个startID,也就是说只有最后的那个startID才能停止掉服务,之前的都不行,明白了吧。其实,这才符合实际,如果你的Service要同时处理多个请求,你就不能在当前一个请求处理完成之后立刻停止Service,因为很可能现在你已经收到了一个新的启动Service请求(如果立刻停止,那么新来的请求就会跟着终止)。为了避免这种情况发生,你可以用stopSelf(int)来保证你当前停止Service的请求是基于上一个请求的。也就是说,当你调用stopSelf(int),你把startID传给了对应的要停止的Service,这个startID是上一个请求的StartID!!如果没有第二个请求来,那么这个Service就会死掉,但是如果这个Service已经又接受到一个新的启动请求之后,你才调用stopSelf(int),那么你传递给stopSelf()的ID是上一个请求的ID,而当前Service的startID已经更新为新的请求的ID,造成两个ID不对应,stopSelf()失效,那么Service就不会停止。这样就避免了将后面的请求终止。
最后总结一下:
- 对于HandlerThread的使用,要先调用其start方法做Looper的初始化操作,然后调用HandlerThread的getLooper方法来获取looper,此方法是个阻塞的方法,直到Looper初始化完成才能被唤醒,最后将looper传入Handle中使用。
- IntentService是系统为我们封装的一个Service,其原理是通过HandlerThread来实现的,我们在使用时只需基础IntentService并实现onHandleIntent方法,在方法中具体处理我们的耗时任务,如果有多个任务,则这些任务会在子线程的消息队列中排队等待被处理,并且处理完最后一个任务时,才会停止掉该服务。
网友评论