IntentService-你可能需要知道这些

Service作为Android四大组件之一,在我们的日常开发中占着不可或缺的一席,而轻量级的IntentService作为对Service的一个补充,也越来越收到大家的推崇,本博文就带大家来学习和分析下IntentService的原理。
IntentService的实现离不开HandlerThread这个大功臣,因此我们还会附带介绍HandlerThread的一些内容。

为什么要使用IntentService

在回答这个问题之前,我们先看下我们常用的Service的特点:

  1. Service是一个不可见的Activity,因此它的几个方法(onCreate\onStartCommand\onBind)是运行在主线程中的,因此不要在Service中做一些重量级的操作,否则可能会导致ANR。(广播BC的onReceive方法也是运行在主线程中,这一点经常在面试中会被问到。)

  2. Service与Thread是存在区别的,Service不是一个单独的线程,也不是一个单独的进程。

  3. 如果不是调用了stopSelf或者stopService,我们启动的Service,可能会一直存在。(会占用系统内存和资源)

分析完Service的特点,我们知道在一些场景下,Service对于我们太重了,我们需要一个更轻量级的服务。

IntentService作为对Service的补充(或者说Service的轻量级实现),很好的弥补了Service的缺陷,我们来看下IntentService的特点:

  1. 启动一个单独的线程(工作线程)来处理任务和请求,所有的任务都在该改线程中处理。

  2. 因为是在单独的线程中处理任务和请求,其onHandleIntent方法运行在单独的线程中,而非主线程,因此可以执行异步操作。(面试中经常被问到)

  3. 按照发送顺序处理任务和请求。当没有任务和请求时,IntentService会自动销毁。因此它不会一直占用资源和内存。

  4. onBind方法的默认实现返回值为null,因此不要尝试调用bindService去调用IntentService。(设计的目的是为了处理简单的异步任务)

这就是我们为什么要使用IntentService的原因。

示例

使用IntentService非常简单,下面给出IntentService的一个简单实现:

public class WorkerService extends IntentService {

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public WorkerService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //TODO:处理任务和请求
        //不要再单独启动线程去处理任务和请求
    }
}

你还要需要做如下操作,才能正常使用IntentService。

  • 在AndroidManifest.xml文件中注册。
  • 像使用Service一样使用IntentService。

HandlerThread

在分析IntentService的源码前,我们先来看下IntentService使用到的一个类:HandlerThread。上源码:

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    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;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    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;
    }

    /**
     * Quits the handler thread's looper.
     * 

* Causes the handler thread's looper to terminate without processing any * more messages in the message queue. *

* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

* Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. *

* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */ public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } /** * Quits the handler thread's looper safely. *

* Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. *

* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

* If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. *

* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */ public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } /** * Returns the identifier of this thread. See Process.myTid(). */ public int getThreadId() { return mTid; } }

从源码的角度,我们得出以下结论:

  1. HandlerThread是一个Thread。(这不废话吗,HandlerThread继承了Tread)

  2. 用于启动一个带有Looper属性线程的方便类。(谷歌的良苦用心,有了HandlerThread我们使用[Hanlder+Thread]就更简单方便了)

那么它与普通的Thread有什么区别呢?

  1. HandlerThread自带Looper,该Looper可直接用于创建Handler。

我们分别来看下使用Thread和使用HandlerThread创建Handller的方式。

1. Thread + Handler

private MyHandler mHandler;

    public void buildHandler() {

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 为当前线程设置Looper
                Looper.prepare();
                // 使用当前线程的Looper构造Handler
                mHandler = new MyHandler(Looper.myLooper());
                // 开始处理MessageQueue
                Looper.loop();
            }
        }).start();

    }

    class MyHandler extends Handler {

        MyHandler(Looper looper){
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }


2. HandlerThread + Handler

private MyHandler mHandler;

    public void buildHandler() {
        // 构造HandlerThread
        HandlerThread handlerThread = new HandlerThread("WorkThread");
        handlerThread.start();
        // 直接使用HandlerThread的looper创建Handler
        mHandler = new MyHandler(handlerThread.getLooper());
    }

    class MyHandler extends Handler {

        MyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }

3. 对比

// 为当前线程设置Looper
Looper.prepare();
// 使用当前线程的Looper构造Handler
mHandler = new MyHandler(Looper.myLooper());
// 开始处理MessageQueue
Looper.loop();

**********************************************************

// 构造HandlerThread
HandlerThread handlerThread = new HandlerThread("WorkThread");
handlerThread.start();
// 直接使用HandlerThread的looper创建Handler
mHandler = new MyHandler(handlerThread.getLooper());

对比很明显,我们在使用HandlerThread创建Handler更简单方便,那么消失的这段代码去哪了?

// 为当前线程设置Looper
Looper.prepare();

// 开始处理MessageQueue
Looper.loop();

答案就是HandlerThread的run()方法自动帮我们完成了,我们来看下HandlerThread的run()方法。

    @Override
    public void run() {
        mTid = Process.myTid();
        // 为当前线程设置Looper
        Looper.prepare();
        synchronized (this) {
            // getLooper()返回的就是mLooper
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        // 开始处理消息队列
        Looper.loop();
        mTid = -1;
    }

相信大家看到这,应该对HandlerThread有了一定了解,并能清晰明了的认识到它与普通Thread的区别。

一探IntentService究竟

本章节带大家来分析下IntentService的原理和实现,知其然还要知其所以然。

IntentService.java

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.
     *
     * 

If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * *

If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } @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); } @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); }

代码非常简洁,而且逻辑也比较清楚,下面我们来详细分析。

  1. 一些属性
    // 工作线程的Looper
    private volatile Looper mServiceLooper;
    // 结合HandlerThread来处理任务和请求
    private volatile ServiceHandler mServiceHandler;
    // Service名称
    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);
        }
    }
  1. 构造方法
 //实现IntentService时构造方法要调用IntentService的构造方法
 public IntentService(String name) {
        super();
        mName = name;
    }
  1. 销毁重启
    如果IntentService存在待处理的任务、或者正在处理任务时候,IntentService所处的进程被杀死,那么将根据mRedelivery的值来决定是否重启后分发Intent。

mRedelivery为true的情况:进程杀死会被重启,且分发未处理完毕的Intent。存在多个Intent的情况下,只会分发最近一次发送的Intent.

mRedelivery为false的情况:进程杀死后不会被重启,Intent随着进程的消亡而消亡。

一般我们在构造方法中,调用如下的方法来设置。

public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
  1. onCreate
   @Override
    public void onCreate() {
        super.onCreate();
        // 创建、启动工作线程
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        // 关联处理任务的Looper和Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

经过这一步,处理任务的工作线程和Handler就已经准备完毕,IntentService处于就绪状态,此时就可以处理发送过来的任务了。

  1. onStartCommand
    //mRedelivery的值决定了方法的返回值。
    //不要重写该方法,去重写onHnadleIntent方法
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
  1. onBind
    // 一般情况下,我们不会重写该方法
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
  1. onHandleIntent
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
  1. 该方法是我们需要重写的方法,它运行在工作线程中。(我们启动的HandlerThread中)

  2. 我们处理任务的地方。(耗时操作等)

  3. 该方法在ServiceHandler的handleMessage中被调用。

  4. onDestory

    @Override
    public void onDestroy() {
        // 退出消息循环
        mServiceLooper.quit();
    }

HandlerThread

原理

经过上个章节的分析,可以清晰的看到IntentService的实现原理就是:
HandlerThread+Handler,是不是感觉so easy.

结束语

IntentService是通过HandlerThread+Handler的方式实现的一个异步操作,可以执行耗时操作同时不会产生ANR的情况,而且具备生命周期管理,是我们执行简单异步任务的一个不错的选择。

你可能感兴趣的:(IntentService-你可能需要知道这些)