美文网首页
HandlerThread和IntentService的使用详解

HandlerThread和IntentService的使用详解

作者: lizb | 来源:发表于2019-01-14 15:36 被阅读0次

在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就不会停止。这样就避免了将后面的请求终止。

最后总结一下:

  1. 对于HandlerThread的使用,要先调用其start方法做Looper的初始化操作,然后调用HandlerThread的getLooper方法来获取looper,此方法是个阻塞的方法,直到Looper初始化完成才能被唤醒,最后将looper传入Handle中使用。
  2. IntentService是系统为我们封装的一个Service,其原理是通过HandlerThread来实现的,我们在使用时只需基础IntentService并实现onHandleIntent方法,在方法中具体处理我们的耗时任务,如果有多个任务,则这些任务会在子线程的消息队列中排队等待被处理,并且处理完最后一个任务时,才会停止掉该服务。

相关文章

网友评论

      本文标题:HandlerThread和IntentService的使用详解

      本文链接:https://www.haomeiwen.com/subject/iysxdqtx.html