本人只是 Android小菜一个,写技术文档只是为了总结自己在最近学习到的知识,从来不敢为人师,如果里面有些不正确的地方请大家尽情指出,谢谢!
1.概述
service
的作用相信大家都是非常熟悉的,主要用来在后台进行任务处理,例如后台播放音乐、下载文件、上传文件等等。由于service
是运行在主线程中的,也有一定的时间限制,如果在主线程中对一个任务的处理时间超过了限制,进程就会出现“应用不响应”,即ANR, Application Not Responding
。为了避免这样情况,都会在service
里用新的thread
处理一些可能需要更多处理时间的任务。
其实Android
早就替我们设计了一种更方便的service + thread
模式,就是本文要讲的IntentService
,通过它可以很方便地实现在service
中使用thread
进行耗时任务的处理。
本文将首先给大家演示下它的基本使用方式,再讲解下IntentService
的内部原理。
2. IntentService 的使用
在知道如何使用前,先看看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.
*
* <p>This "work queue processor" pattern is commonly used to offload tasks
* from an application's main thread. The IntentService class exists to
* simplify this pattern and take care of the mechanics. To use it, extend
* IntentService and implement {@link #onHandleIntent(Intent)}. IntentService
* will receive the Intents, launch a worker thread, and stop the service as
* appropriate.
*
* <p>All requests are handled on a single worker thread -- they may take as
* long as necessary (and will not block the application's main loop), but
* only one request will be processed at a time.
*/
public abstract class IntentService extends Service { ... }
复制代码
相信大家都能很容易看懂这段声明的意思,小菜在这里简单为大家总结下,这么一大段文字主要是说明了两个问题:
IntentService
是什么:用来进行处理异步请求的服务,其内部有一个工作线程,所有发送给服务的请求都会在这个工作线程中按序执行,在处理完所有请求后服务会自动停止。IntentService
如何使用:拓展IntentService
并在其拓展类或者叫子类中实现onHandleIntent(Intent)
接口,在这个接口中进行实际的请求处理,这些请求通过Context.startService(Intent)
来进行发送。
Android SDK
真的可以作为所有SDK
的典范,它会清楚地告诉你“是什么”和“怎么用”的问题,针对相对复杂的情况,还会直接在声明里给出范例。
既然我们已经知道要如何使用IntentService
了,就让我们用一个小例子来演示一下:
public class TestIntentService extends IntentService {
private static final String TAG = "TestIntentService";
private static final String DEFAULT_NAME = "default_name";
// 为了区分不同的请求和方便调用端使用,直接定义了不同的 ACTION.
public static final String DOWNLOAD_ACTION = "com.test.intent.action.DOWNLOAD";
public static final String UPLOAD_ACTION = "com.test.intent.action.UOLOAD";
// 要在 AndroidManifest.xml 里声明 servcie,必须提供一个无参构造函数.
public TestIntentService() {
// IntentService 的构造函数需要提供一个工作线程的名字信息.
super(DEFAULT_NAME);
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
}
@Override
public void onHandleIntent(Intent intent) {
String action = intent.getAction();
// 根据不同的请求类型进行不同的处理,这里只是休眠一段时间,并没有进行实际的处理。
if (DOWNLOAD_ACTION.equals(action)) {
try {
Log.i(TAG, "onHandleIntent, start to download");
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
} else if (UPLOAD_ACTION.equals(action)) {
try {
Log.i(TAG, "onHandleIntent, start to upload");
Thread.sleep(40 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
复制代码
在这段代码里,请求处理函数onHandleIntent(Intent)
会根据接收到的请求进行不同的处理,如果收到的是“下载”请求就休眠30秒模拟下载过程,如果收到的是“上传”请求就休眠40秒模拟上传过程。在写好了service
逻辑后一定不要忘记在AndroidManifest.xml
对其进行注册,否则是无法使用的,注册代码如下:
<service android:name=".TestIntentService" />
复制代码
注意: 这里只是简单地对其进行注册,并没有设置其他相关属性,例如intent-filter
,因为这些和本文所讲内容并无直接关系。
请求的接收和处理代码都已完成,接下来就是发送请求的代码逻辑,如下:
// 发送“下载”请求
Intent downloadIntent = new Intent(this, TestIntentService.class);
downloadIntent.setAction(TestIntentService.DOWNLOAD_ACTION);
startService(downloadIntent);
// 发送“上传”请求
Intent upIntent = new Intent(this, TestIntentService.class);
upIntent.setAction(TestIntentService.UPLOAD_ACTION);
startService(upIntent);
复制代码
现在看当发送这两个“下载”和“上传”请求后,IntentService
是如何响应的:
02-27 12:58:23.100 24190 24190 I TestIntentService: onCreate
02-27 12:58:23.102 24190 24240 I TestIntentService: onHandleIntent, start to download
02-27 12:58:53.107 24190 24240 I TestIntentService: onHandleIntent, start to upload
02-27 12:59:33.115 24190 24190 I TestIntentService: onDestroy
复制代码
可以看到:在发送第一个“下载”请求的时候,service
首先被创建,然后开始处理这个“下载请求”,仅接着第二个“上传”请求也被接收并在处理完第一个请求后开始处理,在处理完所有请求后service
被自动销毁。
3. IntentService 的原理
前面已经讲了如何通过IntentService
实现在工作线程中处理较耗时任务,那么IntentService
内部又是如何实现的呢?本节我们通过分析它的源码来一探究竟。
3.1 创建工作线程
既然IntentService
的功能是在工作线程中处理任务,首先来看看这个工作线程是如何创建出来的。
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);
}
复制代码
当IntentService
第一次启动的时候会调用其onCreate
来完成一些初始化操作:
- 首先创建了一个
HandlerThread
对象,这就是前面一直提到的“工作线程”。大家对Handler
和Thread
都很了解,那这个HandlerThread
是什么呢?简单来说,它就是内部有一个消息循环队列的线程,我们知道默认的线程内部是没有消息循环队列的,这就导致我们无法直接在其内部使用Handler
。Android
为了方便使用,直接提供了一个含有消息循环队列的HandlerThread
。 - 利用已创建的
HandlerThread
内部的消息循环创建一个ServiceHandler
对象,这样它的消息处理函数handleMessage
就会在对应的线程中执行了。
3.2 接收和处理请求
既然工作线程已经创建完成,这时就要考虑如何接收客户端发送过来的请求了,已经了解到客户端是通过startService
来发送请求的,结合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;
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
复制代码
从这段代码看到,onStartCommand
会直接调用onStart
,在这里对发送过来的请求接收并通过mServiceHandler
进行处理。
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
中对接收到的请求用onHandleIntent
进行实际的处理,而onHandleIntent
就是我们在使用过程中必须实现的处理逻辑。
3.3 销毁工作线程
前面提到:当所有请求都被处理完成后,service
就会被销毁,这是如何实现的呢?在上面看到handleMessage
方法里在处理完当前请求时会调用stopSelf(msg.arg1)
来尝试停止当前服务,之所以说“尝试”,是因为它不一定能真正停止服务。还是来看下stopSelf(int)
的实现代码:
/**
* 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) {
}
}
/**
* Stop the service if the most recent time it was started was
* <var>startId</var>. This is the same as calling {@link
* android.content.Context#stopService} for this particular service but allows you to
* safely avoid stopping if there is a start request from a client that you
* haven't yet seen in {@link #onStart}.
*/
public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) {
return false;
}
try {
return mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false;
}
复制代码
在stopSelf(int)
的声明里提到它是stopSelfResult(int)
的老版本,唯一的区别就是没有返回值。那我们直接看stopSelfResult(int)
的声明,其中提到只有在当前的service
的最近一次启动是startId
发起的才会被停止。把这句话放在IntentService
的场景里去理解,如果说当前接收到3个请求,在处理第一个请求后打算去停止服务,但是调用stopSelf()
的时候发现最后一次启动是第三个请求发生的,并不会停止服务;处理完第二个请求后是类似的,只有在处理完第三个请求后,去尝试停止服务,这时发现最近一次启动就是它发起的,可以去停止服务了。停止服务时,其onDestroy
会得到调用:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
复制代码
在这里会停止工作线程的消息循环,等待线程退出。
4. 总结
IntentService
能够接受用户发送的请求在工作线程中顺序处理,处理完成后自动退出,但是由于从 Android O
开始对后台服务增加了更严格的控制,导致当前进程在后台时其含有的后台服务也无法长期存活,IntentService
的使用也有了一定的限制,推荐使用更好的JobIntentService
,感兴趣的同学可以自己去研究。