深入理解IntentService

转载请注明出处:https://blog.csdn.net/android_dong/article/details/81146428

一、 概论

最近看了很多篇讲解IntentService的文章,大部分云里雾里或者没有从原理和源码方面去深入分析,只是简单地show了下使用方法,本篇文章会深入源码,作为自己对IntentService认识的总结。若有错误之处,请指出,多多交流。

IntentService是特殊的Service,它继承了Service并且它是一个抽象类,因此必须要子类实现它才能使用IntentService。IntentService可用于后台耗时任务,当所有任务执行完以后,它会自动销毁,同时,由于它是Service的原因,这就导致它的优先级高于单纯的线程,不容易被系统杀死,所以,IntentService比较适合做一些优先级高的后台任务。在实现上,它封装了HandlerThread和Handler,即新开一个工作线程并实现消息循环,按照启动任务的先后顺序执行,并且下一个任务需要等待上一个执行完成之后才能得到执行。

对于Handler消息机制的讲解可参考:
https://blog.csdn.net/android_dong/article/details/81134692

IntentService与Service的区别:
(1) 它们都是Service,拥有Service的生命周期和基本方法的调用,并且都需要在AndroidManifest.xml文件中声明。
(2) Service运行于启动它的线程中,而IntentService会新开一个线程完成它的工作。
(3) Service不能执行耗时操作,IntentService可以执行耗时操作。

二、基本用法

首先继承IntentService实现onHandleIntent()抽象方法,在这个方法里面执行我们的后台任务,为了方便,这里简单地让它睡5秒,另外,我们需要配置一个无参构造函数调用父类的构造函数传入新开工作线程的名字,以便将来程序调试。为了更直观地查看IntentService的生命周期调用情况,我覆写了其它生命周期方法并打印Log:

public class MyIntentService extends IntentService {

   /*
    * 这里需要指定一个无参构造函数工系统调用,
    * 并且传调用super("MyIntentService"),这里传入的参数会被当做任务线程的名字。
    */
   public MyIntentService() {
      super("MyIntentService");
      Log.d("MyIntentService","MyIntentService");
   }

   @Override
   public IBinder onBind(Intent intent) {
      Log.d("MyIntentService","onBind");
      return super.onBind(intent);
   }

   @Override
   public void onCreate() {
      Log.d("MyIntentService","onCreate");
      super.onCreate();
   }

   @Override
   public void onDestroy() {
      Log.d("MyIntentService","onDestroy");
      super.onDestroy();
   }

   @Override
   public void onStart(Intent intent, int startId) {
      Log.d("MyIntentService","onStart ,startId = " + startId);
      super.onStart(intent, startId);
   }

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      Log.d("MyIntentService","onStartCommand");
      return super.onStartCommand(intent, flags, startId);
   }

   @Override
   public void setIntentRedelivery(boolean enabled) {
      Log.d("MyIntentService","setIntentRedelivery");
      super.setIntentRedelivery(enabled);
   }

   //覆写抽象方法,并执行耗时操作
   @Override
   protected void onHandleIntent(Intent intent) {
      Log.d("MyIntentService","onHandleIntent  Thread.ID " + Thread.currentThread().getId());
      Log.d("MyIntentService","onHandleIntent  Sleep 5s  start");
      try {
         Thread.sleep(5 * 1000) ;
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      Log.d("MyIntentService","onHandleIntent  Sleep 5s  end");
   }
}

在Activity中添加一个Button,当点击Button时去启动IntentService,onClick里的代码如下,这里分为2种情况,执行单个任务和连续执行2个任务。

startService(new Intent(this,MyIntentService.class));

接下来看打印Log:

执行单个任务:
D/MyIntentService: MyIntentService                  //无参构造函数中的打印
D/MyIntentService: onCreate                         //首先调用构造函数设置工作线程的名字     
D/MyIntentService: onStartCommand                   //Service的生命周期方法
D/MyIntentService: onStart ,startId = 1             //Service的生命周期方法
D/MyIntentService: onHandleIntent  Thread.ID 4078   //打印工作线程的ID
D/MyIntentService: onHandleIntent  Sleep 5s  start  //开始执行后台任务,睡5秒
D/MyIntentService: onHandleIntent  Sleep 5s  end    //第一个任务执行完毕
D/MyIntentService: onDestroy                        //自行销毁

连续执行2个任务:
D/MyIntentService: MyIntentService                 //无参构造函数中的打印
D/MyIntentService: onCreate                        //首先调用构造函数设置工作线程的名字
D/MyIntentService: onStartCommand                  //Service的生命周期方法
D/MyIntentService: onStart ,startId = 1            //Service的生命周期方法
D/MyIntentService: onHandleIntent  Thread.ID 4079  //打印工作线程的ID
D/MyIntentService: onHandleIntent  Sleep 5s  start //第一个任务得到执行
D/MyIntentService: onStartCommand                  //此时,第二个任务被启动了
D/MyIntentService: onStart ,startId = 2            //Service的生命周期方法
D/MyIntentService: onHandleIntent  Sleep 5s  end   //第一个任务执行完毕
D/MyIntentService: onHandleIntent  Thread.ID 4079  //打印工作线程的ID
D/MyIntentService: onHandleIntent  Sleep 5s  start //第二个任务开始执行
D/MyIntentService: onHandleIntent  Sleep 5s  end   //第二个任务执行完毕
D/MyIntentService: onDestroy                       //自行销毁

通过示例,我们知道IntentService是按照任务加入顺序执行的,当IntentService中只有一个任务时,执行完后它会自行销毁;当有多个任务时,这些任务在同一个工作线程中(它们的线程ID相等),下一个任务需要等待上一个任务执行完之后才能被执行。

这里解释下setIntentRedelivery()方法,它可以设置Service被意外杀死的时候是否需要重启Service,默认是false。
当设置为true,表示START_REDELIVER_INTENT,服务需要重启并重传Intent
当设置为false,表示START_NOT_STICKY,服务不会重启
如下源码onStartCommand()中:

public void setIntentRedelivery(boolean enabled) {
   mRedelivery = enabled;
}

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

三、源码介绍

接下来到了最激动人心的时刻,去掀开IntentService的神秘面纱!
IntentService类是一个抽象类,子类需要实现onHandleIntent()抽象方法,其内部封装了HandlerThread工作线程和Handler消息发送的处理对象,并且给Handler指定消息的目标线程,将消息发送给HandlerThread的消息队列。当Handler收到消息时去调用onHandleIntent(msg)即执行后台任务,对于源码的介绍,我在源代码中添加了注释。

public abstract class IntentService extends Service {
   private volatile Looper mServiceLooper;
   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.
    */
   public IntentService(String name) {
      super();
      mName = name;
   }

   /**
    * {@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. 如果有多个任务,只恢复最后加入的任务。
    * 

If enabled is false (the default), * {@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(); //初始化工作线程,并且为工作线程设置Name属性,然后启动它 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); //获取工作线程的Looper对象并且为Handler指定消息的目标消息队列, //那么Handler发送的消息就会发往工作线程,由工作线程进行处理。 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() { //摧毁时让looper停止并退出 mServiceLooper.quit(); } //默认返回null ,表示不支持onBind的方式 @Override public IBinder onBind(Intent intent) { return null; } //抽象方法,子类需要覆写并实现后台任务 protected abstract void onHandleIntent(@Nullable Intent intent); }

结合Service的生命周期和上一小节的Demo复述一下连续执行2个任务的调用链:

当第一次startService时,调用链:
onCreate()->onStartCommand->onStart()->mServiceHandler.sendMessage()->mServiceHandler.handleMeesage()->onHandleIntent()

当第二次startService时,由于Service已经被创建并且第一个任务还在执行中,所以就不需要再次调用onCreate()了,调用链:
onStartCommand()->onStart()->mServiceHandler.sendMessage()->(等待第一个任务执行)->mServiceHandler.handleMeesage()->onHandleIntent()->stopSelf()->onDestory()

OK,我相信上面的流程大家都可以看懂,接下来重点看看ServiceHandler的handleMessage()方法

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()中第一个任务执行完成调用了stopSelf后,这个IntentService还在运行,还可以处理第二个任务?既然有疑问,我们就去探究源码,先看Service的stopSelf(int)

public final void stopSelf(int startId) {
   if (mActivityManager == null) {
      return;
   }
   try {
      mActivityManager.stopServiceToken(
            new ComponentName(this, mClassName), mToken, startId);
   } catch (RemoteException ex) {
   }
}

继续查看ActivityManagerService.java中的stopServiceToken方法

@Override
public boolean stopServiceToken(ComponentName className, IBinder token,
                        int startId) {
   synchronized(this) {
      return mServices.stopServiceTokenLocked(className, token, startId);
   }
}

继续查看ActiveServices.java中的stopServiceTockenLocked方法

boolean stopServiceTokenLocked(ComponentName className, IBinder token, int startId) {
   ...省略N行代码...
   if (r.getLastStartId() != startId) {
      return false;
   }
   ...省略N行代码...
}

OK,终于找到原因了!由于调用的是stopSelf(int)传入当前任务的startId,最后调用到ActiveServices的stopServiceTockenLocked进行判断是否可以销毁,这个方法大概的意思就是如果IntentService最新的启动startId和当前正在执行任务的startId不相等,就不能自行销毁,换句话说,还有任务没有处理完,不能销毁。
另外,Service中还提供了另外一个无参stopSelf(),调用这个方法将会无条件自行销毁。

四、总结

IntentService是Android SDK封装好的用于支持后台耗时任务操作的类,它是一个特殊的Service,内部封装了HandlerThread和Handler让其具有工作线程和消息处理的能力,耗时任务在工作线程中执行。我们需要继承抽象类IntentService并实现onHandleIntent()抽象方法,在onHandleIntent()中执行耗时操作。我们只能通过startService的方式来启动IntentSerivce,被启动的任务会按照启动的先后顺序执行,且下一个任务必须要等到上一个任务先执行完成后才能被执行,并且调用者不能干预任务的执行过程。

这里说一下不能使用bindService的方式启动IntentService的原因:
由于IntentService在onStartCommand和onStart方法中进行了工作线程和Handler的初始化工作,然而bindService后涉及生命周期方法不会走到这两个方法。即使在子类中覆写onBind()并返回了IntentService对象,也没有权限手动调用这两个生命周期方法。另外,IntentService中的Handler属性对象是用private修饰的,子类也无权访问,也就不能实现后台耗时任务。

你可能感兴趣的:(Android基础,IntentService,Handler,HandlerThread,后台任务)