Android IntentService的使用与工作原理解析

在Android开发中,我们通常会将一些耗时操作放到一个Service中执行,但是普通的Service默认是运行在主线程中的,我们无法直接在主线程中执行耗时操作,这就需要我们自己在Service中维护一个子线程,相对来说比较麻烦。为了解决这个问题,Google为我们提供了一个IntentService,与普通的Service相比,IntentService会自动创建一个子线程用来执行任务,并且当任务执行完成后,IntentService会自动关闭服务,使用起来非常方便。

IntentService的使用方法

IntentService的使用方法非常简单,举一个简单的例子:

public class IntentServiceExample extends IntentService {

    /**
     * 在构造方法中调用了父类的构造方法IntentService(String name)
     * 参数name用来对工作线程进行命名,方便进行调试
     */
    public IntentServiceExample() {
        super("IntentServiceExample");
    }

    /**
     * 重写父类的onHandleIntent方法,在该方法中实现自己所需的业务逻辑
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        try {
            Log.e("Test", Thread.currentThread().getName() + ":task start");
            Thread.sleep(3000);//让线程休眠3秒,用来模拟耗时任务
            Log.e("Test", Thread.currentThread().getName() + ":task finish");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我们首先要新建一个类来继承自IntentService,然后重写onHandleIntent方法,并在该方法中实现我们所需的业务逻辑,然后我们只需要像启动一个普通Service那样来启动IntentService即可:

Intent intent = new Intent(MainActivity.this,IntentServiceExample.class);
startService(intent);

通过log日志可以看到,onHandleIntent方法是运行在一个子线程中的:

E/Test: IntentService[IntentServiceExample]:task start
E/Test: IntentService[IntentServiceExample]:task finish

需要注意的是使用IntentService也需要在AndroidManifest文件中进行注册,否则IntentService将无法正常工作。

HandlerThread工作原理解析

现在我们已经掌握了IntentService的基本使用方法了,但是在分析IntentService的工作原理之前,我们首先来看一个名为HandlerThread的类,因为HandlerThread是IntentService中非常重要的一个对象,因此了解HandlerThread的原理有助于我们去学习IntentService的工作机制。

从HandlerThread的名字我们就能看出,HandlerThread应该是继承自Thread类,因此它本身也是一个Thread。其中run方法是HandlerThread中一个比较重要的方法,我们来看一下run方法的源码:

public class HandlerThread extends Thread {
    Looper mLooper;

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();   //调用Looper的prepare方法
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();  //调用Looper的loop方法
        mTid = -1;
    }
    ...
}

可以看到,在HandlerThread的run方法中调用了Looper的prepare方法和loop方法。由此我们可以得知,HandlerThread与普通Thread的最大区别就在于,HandlerThread默认开启了该线程中的消息循环系统。(关于Android中的消息系统,不了解的同学可以去看一下我的另一篇博客Android中的消息系统————Handler,MessageQueue与Looper)

HandlerThread还提供了一个getLooper方法,用来获取该线程所对应的Looper:

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // 如果该线程已经被启动,但是尚未开始消息循环,则等待直到消息循环系统启动完成
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

这样我们便可以通过HandlerThread来很方便的来处理一些业务逻辑,IntentService便是一个运用了HandlerThread的典型例子,接下来我们便一起来分析一下IntentService的工作原理。

IntentService的工作原理

在了解了HandlerThread之后,我们终于可以来分析一下IntentService的工作原理了。先列出IntentService的源码:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;

    ...//省略部分代码

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

    @Override
    public void onCreate() {
        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);
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

可以看到IntentService继承自Service,那么IntentService自然也拥有Service的所有生命周期方法。我们首先来看onCreate方法:

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

可以看到在onCreate方法中首先创建并开启了一个HandlerThread对象,前面我们已经分析过了HandlerThread的工作原理,可以得知这个HandlerThread线程会默认开启它的消息系统。之后又通过getLooper方法来获取了这个HandlerThread中的Looper对象,并通过这个Looper对象初始化了一个Handler,即mServiceHandler,因此通过mServiceHandler发送的message最终都会在这个HandlerThread线程中执行。(如果有同学对Android中的Handler与消息系统还不了解,建议先去看一下我的另一篇博客Android中的消息系统————Handler,MessageQueue与Looper)。

然后我们再来看一下IntentService的onStart方法:

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

onStart方法的代码很简单,就是通过之前在onCreate中初始化的mServiceHandler来发送了一条消息。我们来看一下mServiceHandler的代码:

    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的handleMessage方法中直接调用了onHandleIntent方法,由于mServiceHandler是通过HandlerThread线程的Looper来创建的,因此onHandleIntent方法将在HandlerThread中被执行。

在执行完onHandleIntent方法后,IntentService又调用了stopSelf方法来结束了本服务,因此IntentService是一种“用完即走”的服务,无需我们手动去结束服务。

需要注意的是,IntentService的onBind方法默认返回为null,因此我们不建议使用bindService的方法来绑定IntentService:

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

在Android8.0之后,谷歌加强了对后台服务的限制,当应用处于后台时,将无法通过startService开启后台服务,IntentService也受此限制的影响,Google推荐使用JobIntentService来替代IntentService。

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

推荐阅读更多精彩内容