前言

先来说一下我要写这篇文章的原因,前两天下午大概四点钟的时分,我听到坐在我工位斜对面的同事在面试。然后他就问面试者:”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的类引证,ThreadLocalMapThreadLocal中的一个静态内部类:

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作为keyLooper目标作为valueLooper目标直接的存在了当时的线程中。关于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类中的有关注释:
“有关如何创立服务的具体讨论,请阅览服务开发人员指南。已弃用的 IntentServiceAndroid 8.0(API 级别 26)施加的一切后台履行限制的束缚。请考虑运用 androidx.work.WorkManagerandroidx.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);
}

ServiceonCreate()办法中,咱们首要创立了HandlerThread目标,接着咱们调用线程中的start()办法让线程运行起来,这样在HandlerThreadrun()函数中咱们就会创立属于该线程的Looper目标,然后处理Handler发送的音讯。在IntentService内部界说了mServiceLooper变量来保存咱们在HandlerThread中创立的Looper目标,然后咱们运用该Looper目标创立了ServiceHandler目标。
ServiceHandlerIntentService中的一个内部类,具体的代码完成如下:

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(),关于这块的逻辑咱们能够看HandlerdispatchMessage()办法的完成。

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目标,并将intentstartId参数保存到了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运行在后台,有目的处理咱们需求的耗时使命。虽然官方现已不引荐咱们运用该类了,但是这儿作为从前一个面试的知识点,咱们仍是有必要去了解清楚。这儿就当是学习和记忆了。假如对你有帮助的话,记住留个大拇指或许小星星再走哦~