美文网首页
记录-IntentService源码分析

记录-IntentService源码分析

作者: Manza | 来源:发表于2020-06-06 00:42 被阅读0次

1.为什么要使用IntentService?

前提条件是你想使用Service同时又想在Service做一些耗时操作或异步任务处理,这时,使用IntentService比较合适,相较于Service,你不用在Service中重新创建线程,同时你也不用考虑不使用它的时候去调用stopService()方法,下面会讲到这一点。

2.IntentService的定义

 * IntentService is a base class for {@link Service}s that handle asynchronous
 * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.

上面文字的大意就是,IntentService是Service类根据需要处理异步请求(通常用Intent表示)的基类。客户端通过Context的startService(Intent)方法来发送请求。Service根据需要启动,它是通过一个工作线程并按照顺序来处理每个Intent,当执行完所有任务后会stop self(通过调用stopSelf()方法终止服务)。

3.IntentService源码分析

在开始分析源码之前,简单的总结一下我理解的IntentSercice:IntentSercie就是一个可以处理异步任务的Service,内部维护了自己的ServiceHandler和HandlerThread用于消息的传递,并在处理任务结束后自动结束自己的生命周期。
下面我们开始分析它的源码(package android.app;)

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

    /**
     * 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的启动方式和Service是一样的,通常我们使用startService(intent)来启动它。启动IntentService,会依次执行onCreate()->onStartCommand()->onStart()(onStart方法在Service中已经标记为弃用,IntentService重写了该方法,并在onStartCommand中被调用)方法。
首先我们看下OnCreate()方法源码

image.png

可以看到,服务启动后,首先做了一些对于IntentService来说初始化的工作,IntentService之所以可以用来处理异步任务就是由于它内部的ServiceHandler和HandlerThread。上面的代码首先实例化了一个HandlerThread线程(源码这里暂不赘述,有兴趣的话可以看看我的另外一篇文章记录-HandlerThread源码分析
,并通过start()方法来启动该线程。之后通过thread获取到该线程的Looper对象来实例化一个ServiceHandler。
然后我们再看onStartCommand()方法源码

image.png

注释的大意是:你不应该在你的IntentService中重写该方法,相反,应该在你自己定义的IntentService中重写onHandleIntent()方法,onHandleIntent方法会在收到启动请求时由系统调用。可以看到该方法中调用了onStart()方法,并且把intent,startId传递过去。
接着看onStart()方法


image.png

在onStart方法中,通过我们在onCreate()中实例化的ServiceHandler对象在消息池中获取一个消息对象,并把该对象设置给message的target属性(感兴趣的可以看下,通过这种方法生成message可以复用消息池中的对象而不必新建,同时又可以直接把handler对象赋值给message的target属性,在Looper.loop()方法中会用到,此处不赘述),然后通过mServiceHandler的sendMessage方法发送消息。
接下来我们看ServiceHandler的源码

image.png

可以看到handler在收到消息后,做了两件事儿,第一,调用onHandleIntent方法,该方法在IntentService中是一个抽象方法,需要我们的子类自己实现,我们可以在该方法中做一些耗时操作(不必另外开启线程,因为当前该方法就是在上面讲到的HandlerThread中执行的);第二,在执行完onHandleIntent()方法后,IntentService调用了自己的stopSelf()方法来结束自己的生命周期。stopSelf()方法和我们在activity中调用stopService方法一样,都会触发Service的OnDestroy()方法。大家可能注意到,stopSelf方法有个startId的参数,需要注意的是startId必须配对,stopSelf()才会停止服务。

4.常见问题

4.1可以在onHandleIntent()方法中进行异步操作吗?

onHandleIntent()方法本身就是在子线程中执行的,如果在此方法中再执行异步任务,由于onHandleIntent执行完成之后会调用stopSelf()方法,此方法会结束掉当前的IntentService,同时在IntentService的onDestroy方法中mServiceHandler会执行quit()方法,所以导致异步代码的结果无法接收到。

4.2.可以使用bindService()来启动IntentService吗?

image

这是一张Service在不同启动方式下的生命周期图,我们可以看到通过bindService()来启动并不会调用onStartCommand()方法,IntentService中维护的mServiceHandler也就无法发送消息进而onHandleIntent()方法也不会再调用,所以也就和使用普通的Service没有区别了。

4.3.多次启动IntentService会发发生什么?停止服务后,后续的事件还会不会执行?

mServiceHandler是在onCreate中创建的,在Service中,无论是何种启动方式,此方法在整个生命周期中只会执行一次,所以,HandlerThread和ServiceHandler都只实例化了一次,相反,每次启动都会调用onStartCommand()方法,然后把消息加入到消息队列中进行等待并按顺序执行。问题5中已经说到,停止服务后在onDestroy方法中会调用mServiceHandler的quit()方法,导致后续事件无法执行。

相关文章

网友评论

      本文标题:记录-IntentService源码分析

      本文链接:https://www.haomeiwen.com/subject/xhtttktx.html