Android 多线程之 IntentService 源码分析
文章目录
一、前言
这主要是一个讲解 Android 中多线程的系列,全部文章如下:
- Android 多线程之 Handler 基本使用
- Android 多线程之 Handler 源码分析
- Android 多线程之 HandlerThread源码分析
- Android 多线程之 AsyncTask使用源码分析
- Android 多线程之 IntentService使用源码分析
IntentService 从名字可以看出是一个 Service,我们知道 Service 是没有前台页面的,主要用于长时间的后台任务,比如播放音乐等场景。
正常情况下,我们使用 Service 已经足够了,为什么官方又提供了一个 IntentService呢?
这是因为如果我们只使用 Service 的话,Service 默认是运行在主线程的,如果我们需要在 Service 中处理一些耗时任务,那么我们还需要去手动的创建线程或者使用线程池去处理耗时任务,然后在处理完以后手动关闭Service,而 IntentService 已经帮我们做好了这些工作,我们只需要在 onHandleIntent 中写上耗时任务的代码,就可以在子线程中去执行,因为 onHandleIntent 是运行在子线程中的,并且在任务执行完以后,IntentService 会自己执行stopSelf(startId)方法,自行关闭。
二、初识IntentService
2.1 API 文档定义
老规矩,先看下官方文档关于IntentService的介绍

意思是说:
IntentService继承于 Service,并且在需要的时候处理一些异步请求。客户端根据需要通过调用Context.startService(Intent)发送请求来启动,使用工作线程(一般指的是子线程)来依次处理任务,并且在出错的时候自行关闭
这种“工作队列处理器”模式一般用于从主线程拆解任务,IntentService的存在简化了这种模式和处理机制,如果要使用它,只需要继承与IntentService并且实现onHandleIntent(Intent)方法,就可以接收到Intent、开启一个工作线程并且在合适的时机关闭。
所有的请求都运行在同一个工作线程,可能需要更多的时间,但是不会阻塞组线程,一次只处理一个线程
上面是官方 API 对IntentService 的描述,我的理解:
IntentService 本质上是一个 Service,自身封装了异步任务的方法,不需要手动开启子线程去处理异步任务,所有异步任务运行在一个线程中,按照启动顺序串行有序处理,处理完所有任务或者出错后会自动关闭。
2.1 使用时需要关注的方法 onHandleIntent(Intent intent)
onHandleIntent 是运行在子线程的,IntentService自身已经帮助我们封装好了,所以我们使用的时候只需要新建类继承于 IntentService ,并且在onHandleIntent方法里面写上异步任务代码以及处理完任务的操作就可以了。
三、基本使用
下面通过模拟加载本地图片的方式来进行演示
首先新建 UploadImageService:
public class UploadImageService extends IntentService {
public static String IMG_PATH_KEY = "imgPath";
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public UploadImageService() {
super("UploadImageService");
}
public static void startUpload(Context context, String imgPath) {
Intent intent = new Intent(context, UploadImageService.class);
intent.putExtra(IMG_PATH_KEY, imgPath);
context.startService(intent);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null && intent.hasExtra(IMG_PATH_KEY)) {
String path = intent.getStringExtra(IMG_PATH_KEY);
upload(path);
}
}
private void upload(String path) {
try {
// 模拟上传 3 秒结束
Thread.sleep(3000);
sendBroadcastToCaller(path);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void sendBroadcastToCaller(String path) {
Intent intent = new Intent(UPLOAD_RESULT);
intent.putExtra(IMG_PATH_KEY, path);
sendBroadcast(intent);
}
}
在 manifests.xml 中注册
<service android:name=".ui.a.activity.intentservice.UploadImageService" />
Activity代码
public class IntentServiceActivity extends BaseActivity {
public static final String UPLOAD_RESULT = "com.sean.demo.ui.a.activity.UPLOAD_RESULT";
private int i = 0;
private LinearLayout mLyTaskContainer;
private BroadcastReceiver uploadImgReceiver;
private List<String> mUploadPaths;
private AlertDialog mDialog;
private void handleResult(String path) {
TextView tv = mLyTaskContainer.findViewWithTag(path);
mUploadPaths.remove(path);
if (mUploadPaths.size() < 1) {
mDialog.dismiss();
}
tv.setText(path + " upload success ~~~ ");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentLayout(R.layout.activity_intent_service);
setBackArrow();
uploadImgReceiver = new UploadBroadcastReceiver();
mDialog = new AlertDialog.Builder(this).setView(new ProgressBar(this)).create();
mLyTaskContainer = findViewById(R.id.id_ll_taskcontainer);
registerReceiver();
findViewById(R.id.add_task).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addTask();
}
});
}
private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(UPLOAD_RESULT);
registerReceiver(uploadImgReceiver, filter);
}
public void addTask() {
//模拟路径
String path = "/sdcard/imgs/" + (++i) + ".png";
if (null == mUploadPaths) {
mUploadPaths = new ArrayList<>();
}
mUploadPaths.add(path);
UploadImageService.startUpload(this, path);
mDialog.show();
TextView tv = new TextView(this);
mLyTaskContainer.addView(tv);
tv.setText(path + " is uploading ...");
tv.setTag(path);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(uploadImgReceiver);
}
class UploadBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction() != null && intent.getAction().equals(UPLOAD_RESULT)) {
String successPath = intent.getStringExtra(IMG_PATH_KEY);
handleResult(successPath);
}
}
}
}
xml代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_intent_service"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.sean.demo.ui.a.activity.intentservice.IntentServiceActivity">
<Button
android:id="@+id/add_task"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="addTask" />
<LinearLayout
android:id="@+id/id_ll_taskcontainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/add_task"
android:orientation="vertical" />
</RelativeLayout>
效果图:

下面来看下 IntentService的源码:
三、源码分析
其实IntentService源码不是很长,先大体上看下(删除了方法的注释):
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);
}
}
public IntentService(String name) {
super();
mName = name;
}
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();
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);
}
3.1 成员变量
先看下成员变量,主要有:
- Looper 当前
IntentService工作线程的Looper - ServiceHandler 封装的一个继承于
Handler的子类 - mName 名称(不重要)
- mRedelivery 用来设置进程被杀掉以后会执行什么样的操作(控制异常关闭后是否重新启动服务的方式)
3.2流程解析
先看onCreate()(只有在第一次创建服务的时候调用)方法看起:
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
首先,会先创建一个HandlerThread对象,前面的文章已经介绍过了,HandlerThread 主要是为我们提供一个带有Looper的子线程,更加方便的让我们使用。
创建好以后执行 thread.start();去开启线程。
结合 HandlerThread的 run 方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
我们可以知道,这个时候,已经创建好了Looper,并且开启了循环。
然后通过 HandlerThread 的 getLooper() 拿到创建好的 Looper对象,用这个Looper来创建ServiceHandler对象。
总的来说:onCreate里面开启了一个带有Looper对象的子线程,并且通过这个Looper对象创建一个 ServiceHandler对象与之绑定。
接着看onStartCommand(在每次启动服务的时候调用)方法:
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
onStartCommand接收了三个参数:
intent:
启动
Service时传递进来的Intent对象
flags
flags有三个值,分别是0,START_FLAG_REDELIVERY,START_FLAG_RETRY。 分别标志Service在什么时候被启动。
当调用startService来启动服务的时候,这里的值是 0,如果Service发生重启行为,flags就不会是 0 了。(比如你的onStartCommand返回值是START_STICKY或START_REDELIVER_INTENT,则Service终止后会重新启动)。他们的区别在于,如果Service重新启动了,当Service在onStartCommand返回之前就被终止了,那么下次重启时flags的值为START_FLAG_RETRY,当Service在onStartCommand返回后并且在stopSelf(int)还没执行前被终止,那么下次启动时flags的值为START_FLAG_REDELIVERY。
startId:
每次调用
startService向Service传递任务请求时都会生成一个startId,用来代表这一次请求,这个值是唯一的,不管startService调用多少次,每一次的startId的值都是不一样的,这个值和stopSelfResult(int)或者stopSelf(int)一起使用,主要是为了确保Service中的所有任务都已经完成。
方法里面首先调用 onStart方法,
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在 onStart 方法中,会创建一个 Message对象,然后通过mServiceHandler发送出去,然后在 mServiceHandler 的 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);
}
}
protected abstract void onHandleIntent(@Nullable Intent intent);
在这里就看到我们要实现的抽象方法:onHandleIntent,由于创建 ServiceHandler 时候使用的 Looper是在 HandlerThread中创建的,HandlerThread是一个子线程,所以HandlerThread的Looper也是子线程的。
那么与之绑定的ServiceHandler也是子线程绑定的Handler对象,它的 handleMessage 也是运行在子线程的,所以这就解释了为什么onHandleIntent是运行在子线程中了。
再来看下如何自动关闭的。
代码中可以看到,当我们执行完onHandleIntent((Intent)msg.obj);以后,会立即调用 stopSelf(msg.arg1);这里的 msg.arg1 就是在 onStart方法里面组装的startId。
msg.arg1 = startId;
这个 startId 前面在讲onStartCommand参数的时候讲了:
我们都知道,当我们多次调用startService来启动同一个service时,只有第一次创建服务会执行onCreate,然后会多次调用onStartCommand,从而生成不同的startId,一般是从 1 开始,依次递增,这个值是唯一的,不管startService调用多少次,每一次的startId的值都是不一样的,这个值和stopSelfResult(int)或者stopSelf(int)一起使用,当我们调用stopSelf(int startId)时,系统会检测当前服务是否还有其它的startId存在,有的话就不销毁当前service,没有的话则销毁当前服务,这个机制主要是为了确保Service中的所有任务都已经完成才去销毁服务。
而stopSelf()则是立即销毁该 Service。
这里为什么不去掉用 stopSelf(),而要调用 stopSelf(int startId)呢?
因为 stopSelf(msg.arg1); 这行代码是在 onHandleIntent 后面的,那如果我们开启了两个服务来执行上传文件的操作,第一个服务运行结束,如果是使用 stopSelf()那么就直接结束掉Service了,第二个服务就得不到运行,那么如果使用stopSelf(startId)就可以保证在第所有服务运行结束后再关闭Service,能够保证我们的代码能够正确的运行。
最后在 onDestroy()方法里面mServiceLooper.quit();对Looper进行了释放。
3.3 为什么多次启动IntentService 会在一个线程串行执行,执行完才会关闭
3.3.1运行在一个线程
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
其实这个前面已经讲了,IntentService 在 onCreate() 的时候创建了 一个HandlerThread,并且使用HandlerThread对象的 Looper创建了一个 ServiceHandler对象,所以所有的Service实例都是在HandlerThread这个子线程中执行任务的。
3.3.2 按照顺序有序执行
所以当多次启动服务的时候,所有的启动服务传递进来的Intent和启动时生成startId会被封装成 Message对象,由IntentService的成员变量 mServiceHandler发送到mServiceLooper的 MessageQueue中,然后由mServiceLooper的 loop()方法中不断取出 Message交给 mServiceHandler 去处理,这样就保证了任务能够按照启动Service的顺序去执行。
3.3.3 所有任务执行完毕以后再关闭服务
再结合前面分析的 stopSelf(startId),系统会在等待所有Service执行完毕以后才会最终结束服务,在 onDestory()方法中执行mServiceLooper.quit();去释放掉Looper对象。
四、最后
到这里IntentService就算是分析完了,虽然代码不是很多,但是牵扯的内容还是不少的。
在日常开发中,还是要不断的发散思维,在合适的地方用不同的解决方案去解决问题,寻求解决问的最优解。

本文对 Android 多线程中的 IntentService 进行源码分析。介绍了 IntentService 继承于 Service,可处理异步请求,运行在子线程,能自动关闭。通过模拟加载本地图片演示基本使用,详细解析了成员变量、流程,还说明了多次启动时在一个线程串行执行及执行完才关闭的原理。
667

被折叠的 条评论
为什么被折叠?



