Android中IntentService源码解析

Android中IntentService源码解析

一、IntentService的介绍

IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,所以一般情况下我们会它的子类来实现,它一般用于后台耗时的任务,并且当任务执行完毕它会自动停止。此外IntentService是一个Service服务,它的优先级会比普通的线程优先级要高,所以IntentService执行一些高优先级的后台任务,因为他优先级比较高不容易被系统杀死。在实现上,IntentService封装了HandlerThreadHandler

二、IntentService的作用

我们都知道在Android中Service都是运行在主线程中,所以当我们通过startService启动Service之后,我们就需要在Service的onStartCommand方法中写代码完成工作,但是onStartCommand是运行在主线程中的,如果我们需要在此处完成一些网络请求或IO等耗时操作,这样就会阻塞主线程UI无响应,从而出现ANR。为了解决耗时的问题我们一般都会手动创建一个子线程把耗时的任务移到子线程中进行,所以为了简化整个流程,Android特定为你准备了IntentService,IntentService内部自带工作线程HandlerThread而且还能执行完所有任务后可以自己停止。

三、IntentService的特点

  • 1、自带工作线程HandlerThread, 当我们的Service需要做一些可能会阻塞主线程的工作的时候可以考虑使用IntentService。
  • 2、我们需要将要做的实际工作放入到IntentService的onHandleIntent回到方法中,当我们通过startService(intent)启动了IntentService之后,最终Android Framework会回调其onHandleIntent方法,并将intent传入该方法,这样我们就可以根据intent去做实际工作,注意: 并且onHandleIntent运行在IntentService所持有的工作线程中,而非主线程
  • 3、当我们通过startService多次启动了IntentService,这会产生多个job,由于IntentService只持有一个工作线程,所以每次onHandleIntent只能处理一个job。如果存在多个job,IntentService会如何处理?处理方式是one-by-one,也就是一个一个按照先后顺序处理,先将intent1传入onHandleIntent,让其完成job1,然后将intent2传入onHandleIntent,让其完成job2…这样直至所有job完成,所以我们IntentService不能并行地执行多个job,而是串行执行,只能一个一个的按照先后顺序完成,这个根本原因是基于HandlerThread中的Looper中的loop方法是串行从消息队列取消息、处理消息,当所有job完成的时候IntentService就销毁了,会执行onDestroy回调方法

四、IntentService的基本使用

1、首先,定义一个IntentService子类去继承IntentService, 然后重写onHandleIntent方法,这个方法内部执行环境就是工作线程,里面可以执行一些耗时任务,就是需要注意的是不能在该方法内部直接更新UI,需要通过Handler或者其他消息机制来更新UI
class TaskIntentService : IntentService("#TaskService") {
    init {
        Log.d(TAG, "构造器init----current thread is ${Thread.currentThread().name}")
    }

    //TaskIntentService第一次启动时,onCreate被回调
    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate----current thread is ${Thread.currentThread().name}")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand----current thread is ${Thread.currentThread().name}")
        Log.d(TAG, "正在下载...")
        Log.d(TAG, "startId: $startId")
        return super.onStartCommand(intent, flags, startId)
    }

    //注意: onHandleIntent执行在工作线程而非主线程, 内部执行耗时任务
    override fun onHandleIntent(intent: Intent?) {
        intent ?: return
        Log.d(TAG, "onHandleIntent----current thread is ${Thread.currentThread().name}")
        Thread.sleep(3000)
        Log.d(TAG, "下载成功!~~~")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy----current thread is ${Thread.currentThread().name}")
    }

    companion object {
        private const val TAG = "TaskIntentService"
        fun startTask(context: Context) {
            context.startService(Intent(context, TaskIntentService::class.java))
        }
    }
}
2、然后,就像使用普通的Service一样去使用这个IntentService,需要去注册Service,然后在Activity中去启动这个Service即可。注意: 每次调用startService的时候就相当于执行一次onHandleIntent方法,相当于添加一次后台任务,等待这些全部执行完毕后,该Service就会自己把自己杀死。
class TaskServiceActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_task_service)
        btn_add_task.setOnClickListener {
            //每调用一次就相当于向后台线程添加一个后台任务,但这些任务都是串行排队执行,因为内部只有一个工作线程在工作,等待所有任务都执行完毕后,TaskIntentService就会自己停止
            TaskIntentService.startTask(this)
        }
    }
}

五、IntentService的使用场景

一般用于执行后台耗时任务场景,多任务串行执行场景;不支持多任务并行执行, 因为内部只有一个线程在工作

六、IntentService源码分析

  • 1、首先,当IntentService第一次被启动的时候,会触发调用onCreate方法,然后在onCreate方法中创建HandlerThread,由于onCreate只会触发一次,那么HandlerThread只有一个。关于HandlerThread我们都知道它是一个自带消息循环系统的特殊的工作线程。然后再去启动这个线程,最后初始化mServiceHandler,这个Handler的创建是使用HandlerThread的Looper实例作为构造参数的,也就是意味着可以通过这个mServiceHandler可以给HandlerThread工作线程发送消息以及处理该线程内部的消息。

    public void onCreate() {
     super.onCreate();
     //创建一个HandlerThread线程
     HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
     //启动这个线程
     thread.start();
     //然后保存handlerThread的Looper为mServiceLooper
     mServiceLooper = thread.getLooper();
     //通过mServiceLooper创建一个ServiceHandler对象mServiceHandler
     mServiceHandler = new ServiceHandler(mServiceLooper);
     }
  • 2、然后,就会触发调用onStartCommand方法, 然后会委托到内部私有的onStart方法。在onStart方法内部实现就会发现,它会利用mServiceHandlerHandlerThread线程内部发送一个消息,消息体objIntent对象都传递到线程内部,注意Intent一般可用于携带线程内部执行耗时任务的参数,不过最终这个Intent会通过onHandleIntent方法回调出来。

        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            //调用内部的onStart方法,把Intent和startId传入
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    
        @Override
        public void onStart(@Nullable Intent intent, int startId) {
            //通过mServiceHandler的obtainMessage从消息池中取到一个Message实体
            Message msg = mServiceHandler.obtainMessage();
            //把startId和Intent对象数据作为携带数据保存在Message实体中
            msg.arg1 = startId;
            msg.obj = intent;
            //最后利用mServiceHandler的sendMessage方法向HandlerThread线程内部发送一个Message,然后在HandlerThread的Handler的handleMessage中会处理这个消息,这样就从主线程切换到了工作线程HandlerThread
            mServiceHandler.sendMessage(msg);
        }
  • 3、然后,当有Message发送到HandlerThread内部Looper中的消息队列中后,loop()方法内部就会取消息,然后再利用mServiceHandler进行分发消息、处理消息,最后会触发mServiceHandler中的handleMessage方法。

        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
            //注意: 当前的handleMessage方法内部执行环境就是工作线程
            //然后调用onHandleIntent,把Intent对象传递出去,那么onHandleIntent也是工作线程环境,耗时阻塞UI的任务就可以放在onHandleIntent内部执行
                onHandleIntent((Intent)msg.obj);
                //任务执行完毕后,就会调用stopSelf把自己给停止,每个后台任务执行完毕后都会执行stopSelf(startId),注意这里的stopSelf不是立即停止服务,stopSelf(startId)尝试停止服务之前先判断最近一次启动服务id是否和startId相等,相等就停止服务,不相等就不停止服务。
                stopSelf(msg.arg1);
            }
        }
    
        public final void stopSelf(int startId) {
            if (mActivityManager == null) {
                return;
            }
            try {
                mActivityManager.stopServiceToken(
                        new ComponentName(this, mClassName), mToken, startId);
            } catch (RemoteException ex) {
            }
        }

注意:这里使用的是stopSelf(id),而不是使用stopSelf(), stopSelf()会立即停止Service, 因为这时候可能还有其他的消息未处理,stopSelf(id)则会等待所有消息都处理完毕后才会终止服务。stopSelft(id)在尝试停止服务之前会判断最近一次启动服务id是否和startId相等,相等就停止服务,不相等就不停止服务。所以可以看到当启动多个任务时,其中某个任务执行完毕后,会调用stopSelf(id)方法, 但是并没有马上把Service停掉,而是等待所有的任务都执行完毕后,此时startId与最近一次启动服务startId相等就会停止Service

  • 4、为了验证,我们深入ActivityManagerService中的stopServiceToken方法。

      @Override
        public boolean stopServiceToken(ComponentName className, IBinder token, int startId) {
            synchronized(this) {
            //内部调用ActiveServices的stopServiceTokenLocked
                return mServices.stopServiceTokenLocked(className, token, startId);
            }
        }
  • 5、然后再进入ActivityService中的stopServiceTokenLocked方法。

        boolean stopServiceTokenLocked(ComponentName className, IBinder token, int startId) {
            //通过findServiceLocked获得ServiceRecord服务记录对象
            ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
            if (r != null) {
                if (startId >= 0) {
                    ...
                    //通过ServiceRecord拿到最近一次服务的startId,判断是否和外部stopSelf传入startId相等,不相等说明还有后台服务在工作,不能立即停止服务,return false终止执行.
                    if (r.getLastStartId() != startId) {
                        return false;
                    }
                    ...
                }
                //如果相等了就会执行到这里调用stopRunningLocked停止Service
                synchronized (r.stats.getBatteryStats()) {
                    r.stats.stopRunningLocked();
                }
                ...
                return true;
            }
            return false;
        }

   转载规则


《Android中IntentService源码解析》 mikyou 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Android中HandlerThread源码解析 Android中HandlerThread源码解析
Android中HandlerThread源码解析一、HanlderThread介绍 HandlerThread这个类的本质就是一个Thread,它继承自Thread类, 而且是自带Handler的线程。我们都知道在Android主线程中会
2019-12-29
下一篇 
Android中消息机制与Handler源码解析 Android中消息机制与Handler源码解析
Android中消息机制与Handler源码解析 Android中消息机制构成整个Android系统的应用运行基础,我们都知道在每个应用主UI线程都存在一个消息循环系统也就是我们常说的MainLoop. Android应用中每一个交互动作都
2019-12-29