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
就算是分析完了,虽然代码不是很多,但是牵扯的内容还是不少的。
在日常开发中,还是要不断的发散思维,在合适的地方用不同的解决方案去解决问题,寻求解决问的最优解。