IntentService-你可能需要知道这些

作者: 24K男 | 来源:发表于2017-09-27 11:32 被阅读87次

    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.
         * <p>
         * Causes the handler thread's looper to terminate without processing any
         * more messages in the message queue.
         * </p><p>
         * 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.
         * </p><p class="note">
         * 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.
         * </p>
         *
         * @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.
         * <p>
         * 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.
         * </p><p>
         * 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.
         * </p><p>
         * 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.
         * </p>
         *
         * @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.
         *
         * <p>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.
         *
         * <p>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-你可能需要知道这些

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