ThreadLocal、HandlerThread以及IntentService三者关系全面解析

Android SDK里面很多类名都起的让人傻傻分不清楚,本篇文章就是从IntentService这个组件的生命周期出发,深度剖析ThreadLocalHandlerThread以及IntentService之间错综复杂的关系。

首先了解概念

ThreadLocal:线程的本地变量。该变量不与其他线程共享,只能在本线程内操作。

HandlerThread:一种经过包装的Thread。随着该thread启动,其内部的Looper会自动开始循环。

IntentService:一种经过包装的Service。该Service内部会自动开启一个线程用来执行任务,并在任务结束后自动结束。

敲黑板!记住结论:

  1. HandlerThread本质上仍是一个Thread。
  2. IntentService本质上仍是一个Service。

IntentService的生命周期

既然IntentService也是个Service,那么就从它的onCreate方法看起。

IntentService的onCreate方法.png

关注三个点:

  1. 实例化了一个HandlerThread,并启动该线程。
  2. 获取该HandlerThread中自带的Looper对象。
  3. 将获取到的Looper对象作为参数用来实例化ServiceHandler。
HandlerThread部分源码.png

首先,通过阅读HandlerThread的源码,可以发现HandlerThread在run方法中会帮我们创建好一个Looper并让该Looper执行loop方法。在深入理解Handler、Looper与MessageQueue之间的关系一文中,我们详细的描述了Handler、Looper以及MesageQueue三者之间的关系。Looper循环是Handler能在线程中运行的前提条件。
所以我们可以理解为,IntentService的onCreate方法中所用到的HandlerThread,相当于是为IntentService快速的构建并启动了一个带有Looper的工作线程

Looper部分源码.png
其次,Looper在其prepare方法中会通过ThreadLocal将自身与当前线程进行绑定。同样的,在通过Looper.myLooper获取当前线程的Looper对象时,也是通过ThreadLocal进行获取。

最后一点,就是ServiceHandler。

ServiceHandler源码.png

ServiceHandler的构造函数没什么好讲的,和普通Handler一样。重点在于其handleMessage方法。
handlerMessage方法用来处理由Looper循环MessageQueue所得到的Message。此处的代码逻辑为,当有Message发送来,则会调用onHandleIntent方法进行处理,而这个onHandleIntent方法则正是我们使用IntentService时必须要实现的抽象方法。当onHandleIntent方法执行完,便会调用Service的stopSelf方法终止该IntentService,以此达到任务执行完自动结束的效果。

下面放上IntentService的完整代码:

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);
}

最后结合代码完整的梳理一遍IntentService的执行过程:
首先IntentService执行onCreate方法,该方法中会创建一个HandlerThread线程并实例化一个内部类ServiceHandler。
随后,onStartCommand方法会调用onStart方法,在onStart方法内部,会构造一个Message,然后用ServiceHandler来发送该Message。
再之后,ServiceHandler的handleMessage方法会对发来的Mesage进行处理,具体的处理逻辑则是交给抽象方法onHandleIntent来完成,而这个方法正是使用者使用IntentService必须要实现的方法,使用者应该将该Service要执行的任务写在该抽象方法的实现中。
在onHandleIntent方法完成后,便会执行Service的stopSelf(int id)方法,来结束该IntentService。
最后,在IntentService的onDestroy方法里,通过执行mServiceLooper的quit()方法,终止掉该Looper循环,也顺带结束了该IntentService对应的工作线程。

通过整体流程分析,我们可以发现,IntentService为我们封装好了工作线程的初始化、启动以及结束相关的代码,使得开发者能专注于业务逻辑的编写,减少了忘记结束Service所带来的内存泄漏的威胁。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容