IntentService源码学习

327 阅读10分钟

概述

Service作为Android开发常用的组件之一,在上一篇学习笔记中,对Service做了一个简单的学习。明白了Service分为后台服务,前台服务,绑定服务。不管是哪种服务,都是通过继承Service实现的,只是启动方式不一样:后台服务通过startService启动,前台服务通过startForegroundService启动,同时需要指定一个通知和当前的服务进行绑定,绑定服务则通过bindService进行绑定启动。

通常我们使用Service的时候,由于Service也是运行在主线程,所以对于一些耗时操作,我们还是会开启一个或多个工作线程去执行任务,在这种情况下我们就需要自己去管理和维护这些线程,这可能会造成一些危险的操作。而如果我们并不需要同时处理多个任务,Android推荐我们使用IntentService

IntentServiceService的子类,它使用工作线程逐一处理启动请求,在不要求同时处理多个任务的情况下,这是最佳的选择。我们通过继承IntentService类,然后实现其中的onHandleIntent()方法来执行我们需要的操作,这个方法接收每个启动请求的Intent,以便我们传递需要处理的数据。

源码预览

通过上面的介绍,我们已经对IntentService有了一个大概的了解:

  • Service的子类,那么生命周期应该和Service是一致的,应该也支持startServicebindService两种方式启动
  • 使用工作线程说明IntentService已经帮我们实现了在工作线程处理任务
  • 逐一处理启动请求说明每次只能处理一次启动请求,如果存在多个请求,那么首先进来的请求首先会被处理

通过阅读IntentService的源码,我们可以查看是否能够对应上面三点的内容:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    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);
        }
    }
        public IntentService(String name) {
        super();
        mName = name;
    }
    
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    
    @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 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();
    }
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
     @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

可以看到,IntentService的源码非常少,我们按照Service的生命周期函数去查看其中的代码。

onCreate()

  1. onCreate()

    不管通过那种方式启动Service,首先都会执行onCreate()方法,并且这个方法只能被执行一次,我们通常会在这个方法中执行一些初始化的操作,这个方法中现在执行了如下操作:

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

    首先创建了HandlerThread对象,下面是HandlerThread的源码:

    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
        private @Nullable Handler mHandler;
    
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
        public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
        protected void onLooperPrepared() {
        }
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
        @NonNull
        public Handler getThreadHandler() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper());
            }
            return mHandler;
        }
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
        public int getThreadId() {
            return mTid;
        }
    }
    

    可以看到,HandlerThread类是继承自Thread类,那么此处创建这个HandlerThread类的对象也就相当于创建了一个线程对象,接下来调用thread.start()方法也就相当于开启了这个线程。我们知道,调用start()方法的时候会开启一个新的线程去执行run(),下面是run()方法的源码:

        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    

    在新的线程中,首先调用了Looper.prepare()方法,如下是这个方法的源码:

        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(true)这个重载函数,在这里从ThreadLocal中获取Looper对象,如果Looper已经存在了则会抛出异常,否则就创建一个Looper对象,然后保存在ThreadLocal中,ThreadLocal中保存的对象是和当前线程绑定的,在其他线程中无法对这个对象做任何操作。下面进入到创建Looper(true)对象的代码中:

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    在这里又创建了MessageQueue(true)对象,并在Looper中保存了当前线程的对象,下面是创建MessageQueue对象的方法:

        MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();
        }
    

    通过上面的一系列代码,其实我们已经可以了解到,这就是创建了一个消息循环机制,和我们之前使用的Handler是一样的,只不过之前我们一直在UI线程中使用,并没有关心UI线程的消息循环机制是如何创建的。至此,消息循环机制中的LooperMessageQueue都被创建好了。和UI线程中的消息循环机制不同的地方在于,UI线程中的消息循环机制是不能退出的,而这里的消息循环机制是允许退出的。

  2. 继续回到HandlerThread.run()方法中,上面创建好了LooperMessageQueue,接下来的代码如下:

        @Override
        public void run() {
            mTid = Process.myTid();
            //这一步创建好了Looper和MessageQueue
            Looper.prepare();
            synchronized (this) {
            	//将当前Looper保存下来并唤醒线程
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            //这是一个空实现的方法,子类可以实现此方法做一些操作
            onLooperPrepared();
            //调用loop()方法
            Looper.loop();
            mTid = -1;
        }
    

    上面的注释中已经对执行流程写的比较清除了,比较重要的就是notifyAll()Looper.loop()方法,为什么要唤醒线程下一步介绍,而对于Looper.loop()方法,其内部通过一个for循环不断获取MessageQueue中的下一条Message,这是一个死循环,当获取不到Message的时候会退出。

  3. 在上一步中我们已经创建好了消息循环机制,继续看onCreate()中的代码:

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
    

    在这里,首先会获取上一步创建好的Looper,然后创建ServiceHandler(mServiceLooper)将之前创建好的Looper传递进去,下面是HandlerThread.getLooper()方法的源码:

        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
    

    上面的代码中可以看到,如果当前的Looper是空的,那么就会让线程等待,因为我们上面已经提到过了,在执行run()方法时就会创建Looper,配合这里,我们就明白了,如果Looper没有创建好就获取则会出问题,所以这里是一个死循环,如果Looper为空就一直等着,等到Looper创建完成后会调用notify()唤醒线程,此时就能正常获取到Looper了。

    ServiceHandler是继承自Handler,我们需要在创建ServiceHandler的时候传递Looper进去,这样,这个ServiceHandler就知道应该把消息添加到哪个线程的队列当中去了,这一点从Handler的构造函数中可以看出,如下所示:

        public Handler(@Nullable Callback callback, boolean async) {
            ...无关代码...
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    从上面的代码可以看出,如果我们不指定Looper,那么这里会直接使用Looper.myLooper()获取当前线程的Looper,而由于我们的Handler是在主线程中创建的,所以此时如果不传Looper,这里直接就是主线程的Looper,而我们本来希望的是在工作线程处理数据,如果不传这个Looper,很可能出现ANR

    至此,我们就了解了在IntentServiceonCreate()方法中执行了哪些操作,主要就是创建了HandlerThread,也就是创建了一个工作线程,同时为这个线程绑定了事件处理机制。然后创建了一个ServiceHandler,这就是一个Handler,用于提交和处理数据。

onStartCommand()

​ 当我们通过startService()方法启动IntentService后,在执行完onCreate()方法后,接下来就会执行onStartCommand()方法,下面是这个方法的源码:

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

可以看到,在这里是直接调用了onStart()方法,然后根据mRedelivery参数的值返回一个常量。如果mRedeliverytrue,则返回START_REDELIVER_INTENT,这个常量的大概意思是如果Service被意外中止,那么在重新启动这个Service的时候,会将最后一次提交的Intent重新交给这个Service。如果mRedeliveryfalse,则返回START_NOT_STICKY,这个常量的意思是如果service被意外终止,直到重新调用context.startService()方法,才会重新启动这个Service,并且不会提交最后一次的Intent数据。

我们可以在子类中通过调用setIntentRedelivery()方法设置这个值的参数。

onStart()

上面已经了解到,在onStartCommand()方法中其实是调用了onStart()方法,这个方法的源码如下:

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

可以看到,在这里就是创建了一个Message,然后将我们传递的Intent作为参数设置给Message,然后将这个Message发送出去,这里是通过Handler发送出去的,所以直接在Handler中查找处理数据的方法:

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

我们直到,Handler中处理数据的方法是handleMessage,在上面的方法中,直接调用了onHandleIntent()方法,这是需要子类去实现的方法,然后调用了stopSelf()关闭当前的服务。

至此,我们就了解了IntentService全部的工作机制,总的来说就是如下过程:

  1. 首先创建一个线程,并为这个线程创建一个Looper,这样,这个线程就具有了事件传递的能力。
  2. 创建一个Handler,并将上一步线程的Looper传递给这个Handler,这样,通过Handler处理的事件就会在上面创建的工作线程中执行
  3. 通过startService(Intent)启动当前服务,将我们需要处理的数据设置到Intent中,如果是第一次启动服务,此时会先执行onCreate(),接着执行onStartCommand(Intent),否则直接执行onStartCommand(Intent)
  4. IntentServiceonStartCommand(Intent)中会调用onStart(Intent,id)方法,在这个方法里面,会创建一个Message,将包含我们数据的Intent和标志服务的id设置到这个Message中,分别作为Messageobjarg1的参数的值
  5. 通过上面创建的Handler发送第4步创建的Message进入到事件序列中
  6. Handler中获取到需要处理的事件Message会调用onHandleIntent()方法,我们需要实现这个方法书写自己的逻辑
  7. 当服务中的所有事件都被执行完成以后会调用stopSelf()自动关闭服务

至此,整个执行流程就结束了。

通过onBind()启动服务

其实对于通过onBind()方式启动IntentService我个人认为这是不太适合的,因为onBind()方法在启动服务的时候只会调用一次,所以只能在绑定成功后去执行任务,而且由于我们在页面关闭的时候一般会关闭连接,所以就导致任务可能执行不完就被结束了,不过如果我们确实需要这样的效果,也是可以做到的,下面的步骤演示了如何通过onBind()方式绑定一个服务。

  1. 继承IntentService实现自己的Service:

    class MyIntentService : IntentService("MyIntentService") {
    
        private val mBinder by lazy {
            MyIntentServiceBinder()
        }
    
        var mValueListener: IntentServiceStudyActivity.IntentServiceValueListener? = null
    
        override fun onHandleIntent(intent: Intent?) {
            //线程休眠
            Thread.sleep(1 * 1000)
            intent?.let {
                val value = it.getStringExtra("key")
                Logs.e("value is $value")
            }
        }
        
    
        override fun onCreate() {
            Logs.e("onCreate...")
            super.onCreate()
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            Logs.e("onStartCommand...")
            return super.onStartCommand(intent, flags, startId)
        }
    
    
        override fun onDestroy() {
            Logs.e("onDestroy...")
            super.onDestroy()
        }
    
        override fun onBind(intent: Intent?): IBinder {
            super.onStart(intent, 0)
            return mBinder
        }
        
        inner class MyIntentServiceBinder : Binder() {
            fun getService(): MyIntentService = this@MyIntentService
        }
    }
    
  2. Activity中创建连接:

        private val mServiceConnection by lazy {
            object : ServiceConnection {
                override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                    mBindIntentServiceSuccess = true
                    service?.let {
                        for (i in 1 until 10) {
                            val intent = Intent()
                            intent.putExtra("key", "value$i")
                            binder.getService().onBind(intent)
                        }
    
                    }
                }
    
                override fun onServiceDisconnected(name: ComponentName?) {
                    Logs.e("服务连接断开")
                    mBindIntentServiceSuccess = false
                }
    
            }
        }
    

    可以看到,这里连接到服务之后创建了Intent,然后将我们需要的数据设置进去,在这里是直接调用了Service.onBind()方法,其实只要是Service中公开的方法都可以。

    通过上面两步就绑定了服务,也可以正常处理数据,但是onBind()方法不知道能否这样直接调用,暂时没发现什么问题,可以在Service中定义其它的公开的方法去调用。