前言
先来说一下我要写这篇文章的原因,前两天下午大概四点钟的时候,我听到坐在我工位斜对面的同事在面试。然后他就问面试者:"IntentService
有了解过吗?能说一下它是怎么实现的吗?"。接着这个问题后他又问了一个问题:"任务执行完成后,需要手动关闭Service
吗?"。当时其实我也不知道,因为IntentService
在我的印象中面试官也有问过我?当时我也没有回答出来,我记得比较清楚的是,当时的面试官问了我这么一个问题:"你知道IntentService
现在已经不维护了吗?已经被标注为废弃了。"说到这里我就产生了兴趣,那么今天我就把IntentService
的使用和原理分享给大家。希望对你也能有所帮助。
1.HandleThread
在介绍IntentService
之前我们有必要先来介绍一下HandlerThread
这个类的实现。HandlerTread
继承自Thread
,并为我们提供了两个带参数的构造函数,具体的代码如下:
scala
public class HandlerThread extends Thread {
int mPriority;
Looper mLooper;
private Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...
}
这里我们可以看到创建HandlerThread
对象的方式有两种,一种是传递一个name
参数给mPriority
变量设置默认值,一种是传递一个name
参数和一个线程优先级的参数priority
,将传入的priority
赋值给mPriority
。了解了HandlerThread
的创建方式以后我们再来看一下HandelerThread
的一个核心的方法,run()
函数的实现:
ini
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
那么下面我们就来分析一下这段代码。首先第一行代码,我不需要太多的关注,就是获取当前线程的id,然后赋值给当前类的成员变量mTid
。接着我们使用Looper.prepare()
方法在当前线程中创建了一个Looper
对象,我们知道Looper
对象其实是保存在Thread.threadLocals
变量中的,该变量的类型是ThreadLocalMap
。而Looper
对象中持有ThreadLocal
的类引用,ThreadLocalMap
是ThreadLocal
中的一个静态内部类:
arduino
static class ThreadLocalMap { }
在调用Looper.prepare()
方法的时候,就是借助Looper
类中的ThreadLocal
变量来完成Looper
对象的存储。
csharp
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
这里prepare()
方法,又调用了Looper
类中带参的prepare(boolean quitAllowed)
方法。在该方法内部一开始就调用ThreadLocal
类中的get()
方法来判断当前线程中是否已经存储了Looper
对象。
java
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);// 取出当前线程中的ThreadLocalMap对象
if (map != null) { //不为空,取出Looper对象返回
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // 为空,创建ThreadLocalMap对象保存到当前线程,存储Looper对象,再将Looper对象返回
}
如果Looper
对象不为空就直接抛出异常,也就是说在一个线程中Looper.prepare()
方法只能被调用一次,一个线程中只能创建一个唯一的Looper
对象。接着我们打开sThreadLocal.set(new Looper(quitAllowed))
方法:
scss
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
到这里就比较好理解了,首先我们获取当前的线程,然后取出ThreadLocalMap
对象,如果为空我们就创建一个ThreadLocalMap
,并且将创建的ThreadLocalMap
对象赋值给Thread.threadLocals
。
javascript
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这里我们以当前的ThreadLocal
作为key
,Looper
对象作为value
将Looper
对象间接的存在了当前的线程中。关于ThreadLocalMap
的数据结构由于不是这篇文章的重点,这里就不展开介绍了,感兴趣的读者可以自己去了解一下。下面我们接着来介绍run()
函数中的代码:
ini
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
这里就是使用同步代码块将当前线程加锁,然后将刚才存在线程中的Looper
对象保存到IntentService
类中,接着唤醒所有wait()
状态中的线程。
ini
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
接着下面的代码就是设置我们当前线程的优先级,这里提供了一个onLooperPrepared()
的方法,但是并没有具体的实现:
csharp
protected void onLooperPrepared() { }
注释写的也很清楚:如果需要在 Looper
循环之前执行某些设置,可以显式重写该方法。 最后调用Looper.loop()
方法来进行消息的处理。到这里我们就可以很明显的知道HandleThread
这个类的意图了,创建一个新的子线程,用来处理Handler
发出来的消息。
2.IntentServise
了解了HandlerThread的用途,我们再来看IntentService就比较好理解了。
scala
@Deprecated
public abstract class IntentService extends Service { }
这里首先我想将之前面试中遇到的一个问题先抛出来,就是IntentService
类中的有关注释: "有关如何创建服务的详细讨论,请阅读服务开发人员指南。已弃用的 IntentService
受 Android 8.0(API 级别 26)
施加的所有后台执行限制的约束。请考虑使用 androidx.work.WorkManager
或 androidx.core.app.JobIntentService
,它们在 Android 8.0
或更高版本上运行时使用作业而不是服务。另请参阅:androidx.core.app.JobIntentService
"。
意思就是说IntentService
在安卓8.0
以后的版本就不维护了,如果还想使用和IntentService
类似的功能,建议我们使用JetPack
中的WorkManager
或者JobIntentService
。
下面我们就来分析一下IntentService
这个类,首先IntentService
它是一个抽象类,这就意味着我们在使用它之前必须要创建一个类来继承它。
scala
class MyService extends IntentService {
public MyService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
...
}
}
这里我们定义了一个类MyService
继承自IntentService
,并重写了IntentService
中的抽象方法onHandleIntent
。关于这个方法的详细作用,笔者打算放到文章的结尾再来详细介绍。这里我们先来看一下IntentService
中的几个关键的方法:
1. onCreate()
ini
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
在Service
的onCreate()
方法中,我们首先创建了HandlerThread
对象,接着我们调用线程中的start()
方法让线程运行起来,这样在HandlerThread
的run()
函数中我们就会创建属于该线程的Looper
对象,然后处理Handler
发送的消息。在IntentService
内部定义了mServiceLooper
变量来保存我们在HandlerThread
中创建的Looper
对象,然后我们使用该Looper
对象创建了ServiceHandler
对象。
而ServiceHandler
是IntentService
中的一个内部类,具体的代码实现如下:
scala
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);
}
}
ServiceHandler
类中的代码也比较简单,拥有一个带有Looper
类型参数的构造函数,和重写的方法handlerMessage()
。这里我们先来简单分析一下消息的发送流程,我们知道在使用Handler
发送消息的时候,会将当前发送消息的Handler
对象赋值给Message
对象中的target
变量,然后将该消息放入消息队列MessageQueue
中,在Looper.loop()
方法中我们取出该消息,然后调用msg.target.dispatchMessage()
来分发消息,对于正常的情况来说,如果消息的callback
属性没有被赋值,或者创建Handler
对象的时候没有传入Callback
类型的参数,最终我们会调用到我们上面重写的方法handlerMessage()
,关于这块的逻辑我们可以看Handler
中dispatchMessage()
方法的实现。
less
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
2.onStartCommand()、onStart()
less
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
我们知道对于一个Service
来说,onCreate()
方法只会在该Service
创建的时候调用,而onStartCommand()
方法会被多次调用。在onStartCommand()
方法被调用以后,接着这里又调用了onStart()
方法,在onStart()
方法内部我们使用Handler
中的obtainMessage()
方法创建了一个Message
对象,并将intent
、startId
参数保存到了Messgae
对象中。也就是说在我们的Service
启动以后,这里我们就会使用Hanlder
来发一条消息,然后在ServiceHandler
中的handleMessage()
中来处理该消息。
在我们自己定义的MyService
中重写的onHandlerIntent()
方法中取出intent
中的参数,也就是我们想让IntentService
做的事情。这里比如说我们需要下载一张大图片或者想要升级apk等比较耗时的任务,我们都可以放到该方法中去处理。
less
protected void onHandleIntent(@Nullable Intent intent) {
// 取出intent中的参数,做逻辑处理
}
在onHandleIntent()
中的任务处理完成以后,这里会调用stopSelf()
来关闭服务。
scss
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
在服务关闭的时候会调用它的生命周期方法onDestroy()
:
csharp
public void onDestroy() {
mServiceLooper.quit();
}
然后我们使用Looper
对象调用quit()
方法停止处理消息。
总结
分析完IntentService
的使用流程我们再来宏观的看下这个类的意图,启动一个Service
运行在后台,有目的处理我们需要的耗时任务。虽然官方已经不推荐我们使用该类了,但是这里作为曾经一个面试的知识点,我们还是有必要去了解清楚。这里就当是学习和记忆了。如果对你有帮助的话,记得留个大拇指或者小星星再走哦~