【Android】IntentService & HandlerThread源码解析

一、前言

  在学习Service的时候,我们一定会知道IntentService:官方文档不止一次强调,Service本身是运行在主线程中的(详见:【Android】Service),而主线程中是不适合进行耗时任务的,因而官方文档叮嘱我们一定要在Service中另开线程进行耗时任务处理。IntentService正是为这个目的而诞生的一个优雅设计,让程序员不用再管理线程的开启和允许。

  至于介绍HandlerThread,一方面是因为IntentService的实现中使用到了HandlerThread,另一方面是因为IntentService和HandlerThread以及很多Android中的类一样,其实都是为了方便某个目的,对最基本的类进行的一定的扩充,并且结构精巧,便于使用,很适合阅读研究。

 

二、HandlerThread源码

先来一段结结实实的完整源码:

  1 /*
  2  * Copyright (C) 2006 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package android.os;
 18 
 19 /**
 20  * Handy class for starting a new thread that has a looper. The looper can then be 
 21  * used to create handler classes. Note that start() must still be called.
 22  */
 23 public class HandlerThread extends Thread {
 24     int mPriority;
 25     int mTid = -1;
 26     Looper mLooper;
 27 
 28     public HandlerThread(String name) {
 29         super(name);
 30         mPriority = Process.THREAD_PRIORITY_DEFAULT;
 31     }
 32     
 33     /**
 34      * Constructs a HandlerThread.
 35      * @param name
 36      * @param priority The priority to run the thread at. The value supplied must be from 
 37      * {@link android.os.Process} and not from java.lang.Thread.
 38      */
 39     public HandlerThread(String name, int priority) {
 40         super(name);
 41         mPriority = priority;
 42     }
 43     
 44     /**
 45      * Call back method that can be explicitly overridden if needed to execute some
 46      * setup before Looper loops.
 47      */
 48     protected void onLooperPrepared() {
 49     }
 50 
 51     @Override
 52     public void run() {
 53         mTid = Process.myTid();
 54         Looper.prepare();
 55         synchronized (this) {
 56             mLooper = Looper.myLooper();
 57             notifyAll();
 58         }
 59         Process.setThreadPriority(mPriority);
 60         onLooperPrepared();
 61         Looper.loop();
 62         mTid = -1;
 63     }
 64     
 65     /**
 66      * This method returns the Looper associated with this thread. If this thread not been started
 67      * or for any reason is isAlive() returns false, this method will return null. If this thread 
 68      * has been started, this method will block until the looper has been initialized.  
 69      * @return The looper.
 70      */
 71     public Looper getLooper() {
 72         if (!isAlive()) {
 73             return null;
 74         }
 75         
 76         // If the thread has been started, wait until the looper has been created.
 77         synchronized (this) {
 78             while (isAlive() && mLooper == null) {
 79                 try {
 80                     wait();
 81                 } catch (InterruptedException e) {
 82                 }
 83             }
 84         }
 85         return mLooper;
 86     }
 87 
 88     /**
 89      * Quits the handler thread's looper.
 90      * <p>
 91      * Causes the handler thread's looper to terminate without processing any
 92      * more messages in the message queue.
 93      * </p><p>
 94      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 95      * For example, the {@link Handler#sendMessage(Message)} method will return false.
 96      * </p><p class="note">
 97      * Using this method may be unsafe because some messages may not be delivered
 98      * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 99      * that all pending work is completed in an orderly manner.
100      * </p>
101      *
102      * @return True if the looper looper has been asked to quit or false if the
103      * thread had not yet started running.
104      *
105      * @see #quitSafely
106      */
107     public boolean quit() {
108         Looper looper = getLooper();
109         if (looper != null) {
110             looper.quit();
111             return true;
112         }
113         return false;
114     }
115 
116     /**
117      * Quits the handler thread's looper safely.
118      * <p>
119      * Causes the handler thread's looper to terminate as soon as all remaining messages
120      * in the message queue that are already due to be delivered have been handled.
121      * Pending delayed messages with due times in the future will not be delivered.
122      * </p><p>
123      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
124      * For example, the {@link Handler#sendMessage(Message)} method will return false.
125      * </p><p>
126      * If the thread has not been started or has finished (that is if
127      * {@link #getLooper} returns null), then false is returned.
128      * Otherwise the looper is asked to quit and true is returned.
129      * </p>
130      *
131      * @return True if the looper looper has been asked to quit or false if the
132      * thread had not yet started running.
133      */
134     public boolean quitSafely() {
135         Looper looper = getLooper();
136         if (looper != null) {
137             looper.quitSafely();
138             return true;
139         }
140         return false;
141     }
142 
143     /**
144      * Returns the identifier of this thread. See Process.myTid().
145      */
146     public int getThreadId() {
147         return mTid;
148     }
149 }

  总共就149行代码。下面捡重点分析。

  首先,类注释(20-21行)明确指出,该类实现了一个带looper的Thread。23行明确看出HandlerThread是继承于Thread的。那么为什么需要一个Thread带上looper呢?如果想要了解,可以阅读:Android Handler机制,想要深入了解,则可以阅读【Android】Handler、Looper源码分析。简而言之,一个类具有的Looper,就可以接受并且处理消息了。当我们不用HandlerThread而直接使用Thread去实现这样一个功能的时候,需要如下代码:

 1 class LooperThread extends Thread {
 2      public Handler mHandler;
 3 
 4      public void run() {
 5           Looper.prepare();
 6 
 7           mHandler = new Handler() {
 8              public void handleMessage(Message msg) {
 9                  // process incoming messages here
10               }
11          };
12          Looper.loop();
13      }
14 }

  至于其中的Looper.prepare()和Looper.loop()方法起什么作用,读完【Android】Handler、Looper源码分析应该一目了然。OK,很明显,这样创建一个戴Handler的Thread有很多重复的地方,那么怎么复用这些代码,让程序员可以直接而简单的创建呢?我们来看HandlerThread的实现。

  该类注释很明确的说,使用之前必须调用start()方法,Thread类的start()方法调用后会去执行run()方法体,而源码中51-63行则覆盖了Thread的run()方法,注意到这里实现了两行最关键的代码:54和61行。因而一个Thread就具备了Looper的特性。当然,里面为了让用户有足够的可操控性,还设置了一个回调方法。

  另外,读者可能对55-58行感到奇怪:为什么需要synchronized关键字修饰两行代码呢?答案其实在另外一个方法里面:71-86行的getLooper()方法中,getLooper()的时候,线程可能已经启动,但是还没有准备好Looper,而从run()看,其实很快就会准备好,因而这个方法回去wait(),55-58行代码的目的是:一旦准备好Looper,就立马通知被阻塞的线程,防止有别的线程因为调用自身的getLooper()方法而阻塞。

  OK,HandlerThread就介绍到这里。至于怎么使用它,IntentService提供了完美的例子。

 

三、IntentService

同样来一段结实的完整源码:

  1 /*
  2  * Copyright (C) 2008 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package android.app;
 18 
 19 import android.content.Intent;
 20 import android.os.Handler;
 21 import android.os.HandlerThread;
 22 import android.os.IBinder;
 23 import android.os.Looper;
 24 import android.os.Message;
 25 
 26 /**
 27  * IntentService is a base class for {@link Service}s that handle asynchronous
 28  * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 29  * through {@link android.content.Context#startService(Intent)} calls; the
 30  * service is started as needed, handles each Intent in turn using a worker
 31  * thread, and stops itself when it runs out of work.
 32  *
 33  * <p>This "work queue processor" pattern is commonly used to offload tasks
 34  * from an application's main thread.  The IntentService class exists to
 35  * simplify this pattern and take care of the mechanics.  To use it, extend
 36  * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 37  * will receive the Intents, launch a worker thread, and stop the service as
 38  * appropriate.
 39  *
 40  * <p>All requests are handled on a single worker thread -- they may take as
 41  * long as necessary (and will not block the application's main loop), but
 42  * only one request will be processed at a time.
 43  *
 44  * <div class="special reference">
 45  * <h3>Developer Guides</h3>
 46  * <p>For a detailed discussion about how to create services, read the
 47  * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p>
 48  * </div>
 49  *
 50  * @see android.os.AsyncTask
 51  */
 52 public abstract class IntentService extends Service {
 53     private volatile Looper mServiceLooper;
 54     private volatile ServiceHandler mServiceHandler;
 55     private String mName;
 56     private boolean mRedelivery;
 57 
 58     private final class ServiceHandler extends Handler {
 59         public ServiceHandler(Looper looper) {
 60             super(looper);
 61         }
 62 
 63         @Override
 64         public void handleMessage(Message msg) {
 65             onHandleIntent((Intent)msg.obj);
 66             stopSelf(msg.arg1);
 67         }
 68     }
 69 
 70     /**
 71      * Creates an IntentService.  Invoked by your subclass's constructor.
 72      *
 73      * @param name Used to name the worker thread, important only for debugging.
 74      */
 75     public IntentService(String name) {
 76         super();
 77         mName = name;
 78     }
 79 
 80     /**
 81      * Sets intent redelivery preferences.  Usually called from the constructor
 82      * with your preferred semantics.
 83      *
 84      * <p>If enabled is true,
 85      * {@link #onStartCommand(Intent, int, int)} will return
 86      * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
 87      * {@link #onHandleIntent(Intent)} returns, the process will be restarted
 88      * and the intent redelivered.  If multiple Intents have been sent, only
 89      * the most recent one is guaranteed to be redelivered.
 90      *
 91      * <p>If enabled is false (the default),
 92      * {@link #onStartCommand(Intent, int, int)} will return
 93      * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
 94      * dies along with it.
 95      */
 96     public void setIntentRedelivery(boolean enabled) {
 97         mRedelivery = enabled;
 98     }
 99 
100     @Override
101     public void onCreate() {
102         // TODO: It would be nice to have an option to hold a partial wakelock
103         // during processing, and to have a static startService(Context, Intent)
104         // method that would launch the service & hand off a wakelock.
105 
106         super.onCreate();
107         HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
108         thread.start();
109 
110         mServiceLooper = thread.getLooper();
111         mServiceHandler = new ServiceHandler(mServiceLooper);
112     }
113 
114     @Override
115     public void onStart(Intent intent, int startId) {
116         Message msg = mServiceHandler.obtainMessage();
117         msg.arg1 = startId;
118         msg.obj = intent;
119         mServiceHandler.sendMessage(msg);
120     }
121 
122     /**
123      * You should not override this method for your IntentService. Instead,
124      * override {@link #onHandleIntent}, which the system calls when the IntentService
125      * receives a start request.
126      * @see android.app.Service#onStartCommand
127      */
128     @Override
129     public int onStartCommand(Intent intent, int flags, int startId) {
130         onStart(intent, startId);
131         return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
132     }
133 
134     @Override
135     public void onDestroy() {
136         mServiceLooper.quit();
137     }
138 
139     /**
140      * Unless you provide binding for your service, you don't need to implement this
141      * method, because the default implementation returns null. 
142      * @see android.app.Service#onBind
143      */
144     @Override
145     public IBinder onBind(Intent intent) {
146         return null;
147     }
148 
149     /**
150      * This method is invoked on the worker thread with a request to process.
151      * Only one Intent is processed at a time, but the processing happens on a
152      * worker thread that runs independently from other application logic.
153      * So, if this code takes a long time, it will hold up other requests to
154      * the same IntentService, but it will not hold up anything else.
155      * When all requests have been handled, the IntentService stops itself,
156      * so you should not call {@link #stopSelf}.
157      *
158      * @param intent The value passed to {@link
159      *               android.content.Context#startService(Intent)}.
160      */
161     protected abstract void onHandleIntent(Intent intent);
162 }

  同样很短,只有162行。IntentService的目的前面已经叙述了,这边类的注释中也有描述。IntentService实现了"work queue processor",可以将任务剥离主线程(即不会阻塞主线程)并按次序完成任务,当任务完成之后,则会自动关闭自身~听起来非常神奇,非常方便,那如何实现呢?

  我们知道一个Service的声明周期如下:

  

  这两列分别表示两种启动Service的方法:startService和bindService。源码144-147行的实现否决了右边调用bindService启动Service的方法(不是不可用,程序员可以重载IntentService进行进一步的定制,只不过直接使用IntentService的bindService是不能没有意义的~)。既然如此,我们按照左边的生命周期查看源码。

  首先看100-112行的OnCreate()方法,看到没有?HandlerThread!源码在这里创建了一个HandlerThread,HandlerThread的使用方法就在107-111行处~首先是创建实例,然后必须start()(想想run()方法里面干了什么?),接着通过getLooper方法取出Looper用于第111行的ServiceHandler创建,这是一个内部类,继承了Handler而已,重载handleMessage方法,执行两个动作:回调onHandleIntent方法,终止Service。

  128-132行重载了onStartCommand方法,这个方法每次在startService()方法调用的时候都会被执行,它可以根据一个boolean值决定返回值(这里可以去查看一下Service该方法返回值的含义,它决定了Service被杀死之后如何复苏),另外也调用了onStart()方法,即执行114-120行的代码。onStart方法则将传入的intent以及startId包装秤一个msg,交给mServiceHandler发送给HandlerThread的实例去处理。

  OKOK,到这里一口气吃的有点多,停下来整理一下~IntentService到底干了什么?IntentService在内部启动了一个带Looper的Thread,当然,这个Thread也就具备类消息处理的能力,外部每一次调用startService,都会传进一个Intent,而该Intent稍后就会被封装成消息交给HandlerThread处理,处理完毕之后,HandlerThread会主动调用stopSelf()停止服务。

  不知道有没有读者感到奇怪,这里都已经停止服务了,怎么还能继续处理消息呢?其实Service在这里只是主线程和工作线程之间的一个桥梁,Service的Intance唯一性保证了不管调用几次startService,只会有一个工作线程被实例化,从而接受工作,HandlerThread其实是一个一直在等待消息的工作线程,而Service只是负责将任务封装成消息交给它,而每次要传递任务,都必须调用IntentService的startService()方法启动Service,因而在任务执行完毕后关闭Service是没有问题,不会影响后续的任务传递,但是如果任务正在传递中,比如新的任务传递执行到117行,前一个任务刚好执行完毕,这个时候调用stopSelf,即66行,会发生什么呢?官方文档有解释:

However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.

  OK,到这里,关于IntentService的实现也解释完毕了。

 

四、总结

  Android中有很多这样短小精悍的代码,以精妙的设计方式简化了开发过程,非常值得学习,IntentService的实现展现了内部类的作用,内部类和外部类的交互显得自然而又紧密,非常NICE,很值得学习研究~~

转载于:https://www.cnblogs.com/lqminn/p/3795636.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值