Service作为Android四大组件之一,在我们的日常开发中占着不可或缺的一席,而轻量级的IntentService作为对Service的一个补充,也越来越收到大家的推崇,本博文就带大家来学习和分析下IntentService的原理。
IntentService的实现离不开HandlerThread这个大功臣,因此我们还会附带介绍HandlerThread的一些内容。
为什么要使用IntentService
在回答这个问题之前,我们先看下我们常用的Service的特点:
Service是一个不可见的Activity,因此它的几个方法(onCreate\onStartCommand\onBind)是运行在主线程中的,因此不要在Service中做一些重量级的操作,否则可能会导致ANR。(广播BC的onReceive方法也是运行在主线程中,这一点经常在面试中会被问到。)
Service与Thread是存在区别的,Service不是一个单独的线程,也不是一个单独的进程。
如果不是调用了stopSelf或者stopService,我们启动的Service,可能会一直存在。(会占用系统内存和资源)
分析完Service的特点,我们知道在一些场景下,Service对于我们太重了,我们需要一个更轻量级的服务。
IntentService作为对Service的补充(或者说Service的轻量级实现),很好的弥补了Service的缺陷,我们来看下IntentService的特点:
启动一个单独的线程(工作线程)来处理任务和请求,所有的任务都在该改线程中处理。
因为是在单独的线程中处理任务和请求,其onHandleIntent方法运行在单独的线程中,而非主线程,因此可以执行异步操作。(面试中经常被问到)
按照发送顺序处理任务和请求。当没有任务和请求时,IntentService会自动销毁。因此它不会一直占用资源和内存。
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;
}
}
从源码的角度,我们得出以下结论:
HandlerThread是一个Thread。(这不废话吗,HandlerThread继承了Tread)
用于启动一个带有Looper属性线程的方便类。(谷歌的良苦用心,有了HandlerThread我们使用[Hanlder+Thread]就更简单方便了)
那么它与普通的Thread有什么区别呢?
- 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);
}
代码非常简洁,而且逻辑也比较清楚,下面我们来详细分析。
- 一些属性
// 工作线程的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);
}
}
- 构造方法
//实现IntentService时构造方法要调用IntentService的构造方法
public IntentService(String name) {
super();
mName = name;
}
- 销毁重启
如果IntentService存在待处理的任务、或者正在处理任务时候,IntentService所处的进程被杀死,那么将根据mRedelivery的值来决定是否重启后分发Intent。
mRedelivery为true的情况:进程杀死会被重启,且分发未处理完毕的Intent。存在多个Intent的情况下,只会分发最近一次发送的Intent.
mRedelivery为false的情况:进程杀死后不会被重启,Intent随着进程的消亡而消亡。
一般我们在构造方法中,调用如下的方法来设置。
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
- 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处于就绪状态,此时就可以处理发送过来的任务了。
- 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;
}
- onBind
// 一般情况下,我们不会重写该方法
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
- onHandleIntent
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
-
该方法是我们需要重写的方法,它运行在工作线程中。(我们启动的HandlerThread中)
-
我们处理任务的地方。(耗时操作等)
-
该方法在ServiceHandler的handleMessage中被调用。
-
onDestory
@Override
public void onDestroy() {
// 退出消息循环
mServiceLooper.quit();
}
HandlerThread
原理
经过上个章节的分析,可以清晰的看到IntentService的实现原理就是:
HandlerThread+Handler,是不是感觉so easy.
结束语
IntentService是通过HandlerThread+Handler的方式实现的一个异步操作,可以执行耗时操作同时不会产生ANR的情况,而且具备生命周期管理,是我们执行简单异步任务的一个不错的选择。
网友评论