一、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.
*/
总结下来就是:
- IntentService是Service类的子类,用来处理异步请求;
- 客户端可以通过startService(Intent)方法传递请求给IntentService;
- IntentService单独开启了一个线程来处理所有的Intent请求所对应的任务,以免事务处理阻塞主线程,而且任务是按先后顺序逐个进行处理的;
- 当IntentService处理完所有的任务后,它会在适当的时候自动结束服务。
二、IntentService使用步骤
- 继承IntentService,实现构造方法和onHandleIntent()方法;
- 在Manifest文件中注册自己的IntentService类;
- 创建任务请求并发送到IntentService进行处理;
- 通过IntentService向其它组件发送任务处理的结果;
- 在接收的组件中进行后续处理。
三、IntentService使用实例
下面是一个模拟图片上传的demo。
ImgUploadService.java类:
public class ImgUploadService extends IntentService {
public static final String TAG = "ImgUploadService";
private static final String ACTION_UPLOAD_IMG = "com.demo.service.action.UPLOAD_IMAGE";
public static final String EXTRA_IMG_PATH = "com.demo.service.extra.IMG_PATH";
public ImgUploadService(String name) {
super(name);
Log.d(TAG, "ImgUploadService[" + " ThreadName: " + name + " ]");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent");
if (intent != null) {
final String action = intent.getAction();
if (ACTION_UPLOAD_IMG.equals(action)) {
final String path = intent.getStringExtra(EXTRA_IMG_PATH);
handleUploadImg(path);
}
}
}
private void handleUploadImg(String path) {
try {
Thread.sleep(3000); //模拟上传耗时
Intent intent = new Intent(IntentServiceActivity.UPLOAD_RESULT);
intent.putExtra(EXTRA_IMG_PATH, path);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void startUploadImg(Context context, String path) {
Intent intent = new Intent(context, ImgUploadService.class);
intent.setAction(ACTION_UPLOAD_IMG);
intent.putExtra(EXTRA_IMG_PATH, path);
context.startService(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
IntentServiceActivity.java类:
public class IntentServiceActivity extends Activity {
public static final String UPLOAD_RESULT = "com.demo.service.UPLOAD_RESULT";
private LinearLayout mLlContainer;
private TextView mBtnUpload;
int i = 0;
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main_handlerthread);
mLlContainer = (LinearLayout) findViewById(R.id.ll_container);
mBtnUpload = (TextView) findViewById(R.id.btn_upload);
registerReceiver();
mBtnUpload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addTask(); //模拟上传
}
});
}
private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(UPLOAD_RESULT);
LocalBroadcastManager.getInstance(this).registerReceiver(mUploadImgReceiver, filter);
}
public void addTask() {
String path = "图片" + i++ + ".png";
ImgUploadService.startUploadImg(this, path);
TextView tv = new TextView(this);
mLlContainer.addView(tv);
tv.setText(path + " ....正在上传中....");
tv.setTag(path);
}
private void handleResult(String path) {
TextView tv = (TextView) mLlContainer.findViewWithTag(path);
tv.setText(path + " ----上传成功---- ");
}
private BroadcastReceiver mUploadImgReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(UPLOAD_RESULT)) {
String path = intent.getStringExtra(ImgUploadService.EXTRA_IMG_PATH);
handleResult(path);
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mUploadImgReceiver);
}
}
activity_main.xml文件:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:text="发送上传请求"/>
</LinearLayout>
四、IntentService源码解析
IntentService本身就是一个Service,它拥有Service的所有生命周期方法,但内部是通过HandlerThread实现异步执行任务的,HandlerThread是一个内部维护了一个消息队列的线程,对于这一点,可查看博客:Android多线程开发之HandlerThread的使用。
既然IntentService有生命周期,那么就从它的构造函数看起:
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
构造方法很简单,用于创建一个IntentService对象,参数namne用于定义工作线程的名字,仅用于调试作用。接下来看下IntentService的onCreate()方法:
@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();
// 创建一个HandlerThread对象,并传入工作线程的名字
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
// 开启后台工作线程
thread.start();
// 获取后台工作线程的Looper对象
mServiceLooper = thread.getLooper();
// 创建一个ServiceHandler对象,用来处理异步消息。
mServiceHandler = new ServiceHandler(mServiceLooper);
}
在onCreate()方法里,首先利用HandlerThread类创建了一个循环的工作线程,接着获取工作线程中的Looper对象并将其作为参数创建了一个叫ServiceHandler的类,该类是IntentService的内部类,该类继承了Handler,我们看它的定义:
public abstract class IntentService extends Service {
//volatile关键字保证变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”中读取
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()中进行处理
onHandleIntent((Intent)msg.obj);
//处理完消息就调用stopSelf()方法,并传入消息的索引值
stopSelf(msg.arg1);
}
}
}
从源码中可以看到,mServiceLooper和mServiceHandler都加了volatile关键字修饰,这是为了保证变量在每次使用的时候都从主内存中读取,而不是从各个线程的“工作内存”中读取,也就是说保证内存的可见性。
接着看onHandleIntent()方法,该方法是在ServiceHandler收到消息后回调的,也即onHandleIntent()方法是在HandlerThread工作线程中执行的,它是一个抽象方法,留给调用者去实现耗时任务的。
/**
* 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)}.
*/
protected abstract void onHandleIntent(Intent intent);
此外,因为任务是通过ServiceHandler发送给异步线程的消息队列来处理的,而消息队列里的消息是依次取出并执行的,所以从这里可以了解到任务必定是串行执行的。而执行完一个消息后,调用了一个带有startId参数的stopSelf()方法:
** * 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) {
}
}
该方法实际上是通过调用ActivityManager的stopServiceToken()方法来停止当前服务的,不过服务不会马上停止,而是等待继续完成剩下的任务后才自动结束,因为方法参数里携带了一个startId,它可以看做是一个请求的唯一标识,只有当所有的请求都结束,我们的Service才自行销毁,而startId是从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(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
对于Service,每次调用startServcie方法启动一个服务,都会调用一次onStartCommand()方法,每次也会带一个startId过去,如果执行多个任务,那么就会存在多个startId,然后在onStart()方法中通过mServiceHandler获得一个消息对象msg,然后将startId作为该消息的消息码,将异步任务请求intent作为消息内容封装成一个消息msg发送到mServiceHandler中进行异步处理,最后回调到onHandleIntent()中,子类通过实现onHandleIntent()即可进行异步耗时任务的执行。
这里注意,我们不需要自己去重写onStartCommand()和onStart()方法,但onHandleIntent()必须重写。
当所有的startId都处理完后,IntentService会调用onDestroy()自行销毁,消息队列也会自行退出。
@Override
public void onDestroy() {
mServiceLooper.quit();
}
这里还有两个方法,留给子类必要的时候去实现它们,分别是setIntentRedelivery()和onBind()方法:
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@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.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
setIntentRedelivery()表示设置是否重新派发,通常在构造方法中进行设置。如果设置为true,onStartCommand()将会返回START_REDELIVER_INTENT,如果在onHandleIntent()返回之前进程已经死掉了,那么进程将会重新启动,intent会重新发送,如果有大量的intent发送了,那么只会保证最近的intent会被重新派发。如果设置为false(也即默认的值),那么onStartCommand()将会返回START_NOT_STICKY,这时如果进程死了,Intent也会停止传递。
对于onBind()方法,默认返回了null,除非我们是通过bindService()启动的服务,否则我们也不需要实现它。
五、IntentService应用场景
从前面的分析可以知道IntentService有以下特点:
- IntentService继承自Service,任务的执行都是以队列的形式进行的,而且不受UI生命周期的影响;
- 创建了一个独立的工作线程来处理onStartCommand()发送过来的任务,并逐个排队进行处理;
- 不需要自己主动启动和关闭线程;
- 不需要主动调用stopSelf()来结束服务,任务处理完后会自动进行关闭;
但也有以下局限性:
- 不能直接和UI进行交互,需自行处理线程之间的通信;
- 工作任务是顺序执行的,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理;
- 正在执行的任务无法被打断;
所以IntentService只适合处理简单的顺序执行的后台耗时任务,不适合处理高并发的后台耗时任务。比如多文件下载,多图片上传等都可用IntentService进行处理,处理完后若需要进行通知,可通过广播或者其它一些线程切换工具进行异步通知,比如EventBus等。