概述
Service作为Android开发常用的组件之一,在上一篇学习笔记中,对Service做了一个简单的学习。明白了Service分为后台服务,前台服务,绑定服务。不管是哪种服务,都是通过继承Service实现的,只是启动方式不一样:后台服务通过startService启动,前台服务通过startForegroundService启动,同时需要指定一个通知和当前的服务进行绑定,绑定服务则通过bindService进行绑定启动。
通常我们使用Service的时候,由于Service也是运行在主线程,所以对于一些耗时操作,我们还是会开启一个或多个工作线程去执行任务,在这种情况下我们就需要自己去管理和维护这些线程,这可能会造成一些危险的操作。而如果我们并不需要同时处理多个任务,Android推荐我们使用IntentService。
IntentService是Service的子类,它使用工作线程逐一处理启动请求,在不要求同时处理多个任务的情况下,这是最佳的选择。我们通过继承IntentService类,然后实现其中的onHandleIntent()方法来执行我们需要的操作,这个方法接收每个启动请求的Intent,以便我们传递需要处理的数据。
源码预览
通过上面的介绍,我们已经对IntentService有了一个大概的了解:
Service的子类,那么生命周期应该和Service是一致的,应该也支持startService和bindService两种方式启动- 使用工作线程说明
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()
-
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线程的消息循环机制是如何创建的。至此,消息循环机制中的Looper和MessageQueue都被创建好了。和UI线程中的消息循环机制不同的地方在于,UI线程中的消息循环机制是不能退出的,而这里的消息循环机制是允许退出的。 -
继续回到
HandlerThread.run()方法中,上面创建好了Looper和MessageQueue,接下来的代码如下:@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的时候会退出。 -
在上一步中我们已经创建好了消息循环机制,继续看
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。至此,我们就了解了在
IntentService的onCreate()方法中执行了哪些操作,主要就是创建了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参数的值返回一个常量。如果mRedelivery为true,则返回START_REDELIVER_INTENT,这个常量的大概意思是如果Service被意外中止,那么在重新启动这个Service的时候,会将最后一次提交的Intent重新交给这个Service。如果mRedelivery为false,则返回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全部的工作机制,总的来说就是如下过程:
- 首先创建一个线程,并为这个线程创建一个
Looper,这样,这个线程就具有了事件传递的能力。 - 创建一个
Handler,并将上一步线程的Looper传递给这个Handler,这样,通过Handler处理的事件就会在上面创建的工作线程中执行 - 通过
startService(Intent)启动当前服务,将我们需要处理的数据设置到Intent中,如果是第一次启动服务,此时会先执行onCreate(),接着执行onStartCommand(Intent),否则直接执行onStartCommand(Intent) - 在
IntentService的onStartCommand(Intent)中会调用onStart(Intent,id)方法,在这个方法里面,会创建一个Message,将包含我们数据的Intent和标志服务的id设置到这个Message中,分别作为Message的obj和arg1的参数的值 - 通过上面创建的
Handler发送第4步创建的Message进入到事件序列中 Handler中获取到需要处理的事件Message会调用onHandleIntent()方法,我们需要实现这个方法书写自己的逻辑- 当服务中的所有事件都被执行完成以后会调用
stopSelf()自动关闭服务
至此,整个执行流程就结束了。
通过onBind()启动服务
其实对于通过onBind()方式启动IntentService我个人认为这是不太适合的,因为onBind()方法在启动服务的时候只会调用一次,所以只能在绑定成功后去执行任务,而且由于我们在页面关闭的时候一般会关闭连接,所以就导致任务可能执行不完就被结束了,不过如果我们确实需要这样的效果,也是可以做到的,下面的步骤演示了如何通过onBind()方式绑定一个服务。
-
继承
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 } } -
在
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中定义其它的公开的方法去调用。