前言
先来说一下我要写这篇文章的原因,前两天下午大概四点钟的时分,我听到坐在我工位斜对面的同事在面试。然后他就问面试者:”IntentService
有了解过吗?能说一下它是怎样完成的吗?”。接着这个问题后他又问了一个问题:“使命履行完结后,需求手动封闭Service
吗?”。其时其实我也不知道,因为IntentService
在我的印象中面试官也有问过我?其时我也没有回答出来,我记住比较清楚的是,其时的面试官问了我这么一个问题:“你知道IntentService
现在现已不保护了吗?现已被标注为废弃了。”说到这儿我就产生了兴趣,那么今天我就把IntentService
的运用和原理分享给大家。希望对你也能有所帮助。
1.HandleThread
在介绍IntentService
之前咱们有必要先来介绍一下HandlerThread
这个类的完成。HandlerTread
承继自Thread
,并为咱们提供了两个带参数的结构函数,具体的代码如下:
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()
函数的完成:
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
中的一个静态内部类:
static class ThreadLocalMap { }
在调用Looper.prepare()
办法的时分,便是借助Looper
类中的ThreadLocal
变量来完结Looper
目标的存储。
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
目标。
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))
办法:
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
。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这儿咱们以当时的ThreadLocal
作为key
,Looper
目标作为value
将Looper
目标直接的存在了当时的线程中。关于ThreadLocalMap
的数据结构因为不是这篇文章的重点,这儿就不展开介绍了,感兴趣的读者能够自己去了解一下。下面咱们接着来介绍run()
函数中的代码:
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
这儿便是运用同步代码块将当时线程加锁,然后将方才存在线程中的Looper
目标保存到IntentService
类中,接着唤醒一切wait()
状况中的线程。
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
接着下面的代码便是设置咱们当时线程的优先级,这儿提供了一个onLooperPrepared()
的办法,但是并没有具体的完成:
protected void onLooperPrepared() { }
注释写的也很清楚:假如需求在 Looper
循环之前履行某些设置,能够显式重写该办法。
终究调用Looper.loop()
办法来进行音讯的处理。到这儿咱们就能够很明显的知道HandleThread
这个类的目的了,创立一个新的子线程,用来处理Handler
发出来的音讯。
2.IntentServise
了解了HandlerThread的用处,咱们再来看IntentService就比较好理解了。
@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
它是一个抽象类,这就意味着咱们在运用它之前必须要创立一个类来承继它。
class MyService extends IntentService {
public MyService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
...
}
}
这儿咱们界说了一个类MyService
承继自IntentService
,并重写了IntentService
中的抽象办法onHandleIntent
。关于这个办法的具体效果,笔者计划放到文章的结尾再来具体介绍。这儿咱们先来看一下IntentService
中的几个关键的办法:
1. onCreate()
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
中的一个内部类,具体的代码完成如下:
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()
办法的完成。
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()
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等比较耗时的使命,咱们都能够放到该办法中去处理。
protected void onHandleIntent(@Nullable Intent intent) {
// 取出intent中的参数,做逻辑处理
}
在onHandleIntent()
中的使命处理完结今后,这儿会调用stopSelf()
来封闭服务。
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
在服务封闭的时分会调用它的生命周期办法onDestroy()
:
public void onDestroy() {
mServiceLooper.quit();
}
然后咱们运用Looper
目标调用quit()
办法停止处理音讯。
总结
剖析完IntentService
的运用流程咱们再来宏观的看下这个类的目的,发动一个Service
运行在后台,有目的处理咱们需求的耗时使命。虽然官方现已不引荐咱们运用该类了,但是这儿作为从前一个面试的知识点,咱们仍是有必要去了解清楚。这儿就当是学习和记忆了。假如对你有帮助的话,记住留个大拇指或许小星星再走哦~