多个任务在IntentService中是如何被顺序执行的

使用过IntentService的都清楚,这是一个继承自Service,内部实现了HandlerThread来进行异步处理任务。因为Service默认是运行在主线程的,通过内部启动一个HandlerThread,和一个关联了该thread的handler来将该service得到的intent放入到线程中执行。我们实现时候,创建一个继承自IntentService的类,只需要实现其中的onHandleIntent(intent)方法,对拿到的intent做处理,该方法默认是在一个工作线程中执行的,执行完成后会自动销毁service。

IntentService就是方便我们在后台做异步任务使用的。

网上很多文章都说,intentService能够顺序的执行多个任务,这怎么理解呢?按照原来的理解执行完onHandleIntent方法后不就应该销毁了么,难道会一直等待任务,如果是等待的话,这个时间是多久呢?不可能一直等着不销毁吧,这不符合intentService的定义。究竟是怎么一回事,我们还得从源码里捋一捋。

首先我们看下类的定义:

public abstract class IntentService extends Service 

是一个继承自Service的抽象类,其中只有一个抽象方法:

/**
     * 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的时候就要实现它。

另外该类的onBind方法是放回空的:

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

所以限定了我们只能以startService的方法来启动这个service。(当然用bind的方法也是可以的,这就需要我们自己实现onBind方法了,但是这样做的话,intentService就没啥用处了,直接继承Service就好了)。

一般我们在activity中执行startService后,service执行的流程是onCreate,onStartCommand。所以我们先来看下onCreate方法内部干了啥:

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); // 创建一个关联到上面线程的handler
    }

就做了两件事:

1,使用HandlerThread开启一个线程

2,创建一个关联了该线程的mServiceHandler对象,这样我们使用这个mServiceHandler发送一个消息的时候,会把消息放入线程的消息队列中,然后通过线程中的looper对象不断的取消息队列中的消息,取到之后回调mServiceHandler的handleMessage来处理消息。这个消息处理的模型我们是不是很熟悉,对的,它就是UI线程消息处理的模型,只不过现在这个消息的接收和分发都放到了工作线程中来处理。

总结来说,就是在service的onCreate中,创建了handler的消息模型,该消息模型运行在一个工作线程中,静静的等待着我们发消息,然后处理消息。。。

接下来我们看看service生命周期的第二个方法,onStartCommand:

/**
     * 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;
    }

直接调用了onStart方法:

public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage(); // onCreate中创建的handler出现了,从msg池中复用取出一个msg
        msg.arg1 = startId; // 记录startId,这个很关键,在后面的分析中会有意想不到的作用
        msg.obj = intent; // 保存intent
        mServiceHandler.sendMessage(msg); // 发送消息到工作线程的消息队列中
    }

这里也是非常简单呢,用一个msg记录了启动的intent和一个startId,这个startId是当我们每次startService的时候,系统为我们分配的id,用来区别是哪次startService。我们在学习service的知道,startService是可以多次调用的,每调用一次就会回调一次onStartCommand方法。

从这个方法中我们知道,这里是将获取到的intent和startid通过handler发送到工作线程中的消息队列中,这个方法的执行还是在主线程(UI线程)。既然这里有发送消息,那么必然后处理消息啊。我们知道handler处理消息是在handleMessage方法中,所以我们要看看mServiceHandler这个对象的handleMessage做了啥:

通过onCreate方法的分析,我们知道在这里面创建了一个mServiceHandler对象,该对象是一个ServiceHandler对象,我们在IntentService中找到了这个私有内部类:

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

其中的handleMessage是我们要重点关注的,在这个方法里面做了两个事情:

1,调用intentService的onHandlerIntent方法将msg携带的intent传进去,这个方式是我们继承的时候自己处理的。

2,执行完onHandleIntent后,执行停止service的方法。

乍一看,这里执行完了stopSelf后,service不是应该就被停止了么?不一定,玄机就在于这个方法。

关于这个方法,有两个重载,一个是无参的,一个是有参的,其中无参的方法调用的是有参的方法,并且传入startId为-1:

/**
     * Stop the service, if it was previously started.  This is the same as
     * calling {@link android.content.Context#stopService} for this particular service.
     *  
     * @see #stopSelfResult(int)
     */
    public final void stopSelf() {
        stopSelf(-1);
    }

    /**
     * Old version of {@link #stopSelfResult} that doesn't return a result.
     *  
     * @see #stopSelfResult
     */
    public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }

内部是调用了AMS的stopService的流程。service的生命周期是AMS管理的,在调用stopSelf(int startId)的过程中,系统会检测是否还有startId存在,如果存在,则不销毁service,否则销毁service。当然如果你直接调用stopSelf(-1),那么将直接销毁service,系统就不会检测是否还有其他的startId存在。这个startId就是我们在onStartCommand中被分配的参数,系统为每一次的startService都分配了一个id。

所以即便我们在handleMessage中执行了stopSelf也不一定会立马结束service,这要看系统中是否还存在startid。

通过以上分析,我们可以知道在IntentService中顺序执行任务是啥意思?因为用的是消息队列,遵循先进先出的逻辑,所以保证了是顺序执行的;然后,什么是多个任务呢?就是多次启动了startService,每启动一次算一个任务msg入队。

其实要保证顺序执行完所有任务再退出service是有个前提的,就是在某个任务执行stopSelf前,必须有任务加入到了消息队列中(即startService)。如果我们每个任务开启的时间间隔过长,即每次startService间隔过长(具体时间长度依赖onHandlerIntent的执行时间),service将每次都会执行onDestroy,然后再执行onCreate,然后在onDestroy。。。

举个例子,假如onHandlerIntent的执行时间是5s,每次启动这个service的时间间隔是3s,那么第一次执行startService的时候,该service会依次执行onCreate,onStartCommand,然后onHandlerIntent,在执行onHandlerIntent的过程中,假如此时执行了3s,这时第二次执行startService,这个时候会执行service的onStartCommand,mServiceHandler将intent和startid包装成msg入消息队列,等待被执行。onHandlerIntent继续执行2s后,就到了stopSelf这里,系统检测出有新的startid(也就是我们第二次startService时候系统分配的startid),不会执行service的销毁,这个时候消息handler的handleMessage方法执行完了,然后继续从消息队列中取出第二次startService时候得到的intent执行,这样反复循环知道消息。

假如onHandlerIntent的执行时间还是5s,但是每次启动service的时间间隔是10s,那么必然执行完第一次startService后,处理完onHandlerIntent的业务后,系统就会去销毁service了。

 

结论:所谓的intentService顺序执行完所有任务再进行销毁,前提是在执行某个stopSelf的时候,当前系统检测到还有startid存在,即startService被及时调用,否则将会按照service正常的生命周期结束自己。保证顺序执行完所有任务的核心就是handler的模型和AMS对service的管理。

 

最后,贴一下两种测试的日志:

第一种假设,startService时间间隔较短,等到处理完所有的startService的intent后才执行onDestroy:

2019-04-17 19:30:23.960 3848-3848/com.txt.mydemo E/IntentServiceTestActivity: startService
2019-04-17 19:30:23.986 3848-9664/com.txt.mydemo I/myIntentService: onHandleIntent
2019-04-17 19:30:24.192 3848-3848/com.txt.mydemo E/IntentServiceTestActivity: startService
2019-04-17 19:30:25.989 3848-9664/com.txt.mydemo I/myIntentService: 滞后2s---->null
2019-04-17 19:30:25.994 3848-9664/com.txt.mydemo I/myIntentService: onHandleIntent
2019-04-17 19:30:27.997 3848-9664/com.txt.mydemo I/myIntentService: 滞后2s---->null
2019-04-17 19:30:28.002 3848-3848/com.txt.mydemo I/myIntentService: onDestroy

第二种假设,startService时间间隔较长,会按照正常的生命周期来执行:

2019-04-17 19:32:49.562 3848-3848/com.txt.mydemo E/IntentServiceTestActivity: startService
2019-04-17 19:32:49.585 3848-9723/com.txt.mydemo I/myIntentService: onHandleIntent
2019-04-17 19:32:51.587 3848-9723/com.txt.mydemo I/myIntentService: 滞后2s---->null
2019-04-17 19:32:51.593 3848-3848/com.txt.mydemo I/myIntentService: onDestroy
2019-04-17 19:32:52.451 3848-3848/com.txt.mydemo E/IntentServiceTestActivity: startService
2019-04-17 19:32:52.470 3848-9724/com.txt.mydemo I/myIntentService: onHandleIntent
2019-04-17 19:32:54.472 3848-9724/com.txt.mydemo I/myIntentService: 滞后2s---->null
2019-04-17 19:32:54.479 3848-3848/com.txt.mydemo I/myIntentService: onDestroy

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值