一. 前言

1.多线程常用的主要方法

  • 继承Thread类

  • 实现Runable接口

  • AsyncTask

  • Handler

  • HnadlerThread

  • IntentService

2. IntentService的作用

处理异步请求 和 实现多线程

3. 使用场景

线程任务需要按顺序,在后台执行。

最常见的场景:离线下载。

4. 工作原理

若启动IntentService 多次,那么 每个耗时操作 则 以队列的方式IntentServiceonHandleIntent回调方法中依次执行,执行完自动结束。

二. 源码分析

1. IntentService 如何单独开启一个新的工作线程

主要分析内容 = IntentService源码中的 onCreate()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
public void onCreate() {
super.onCreate();

// 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
// HandlerThread继承自Thread,内部封装了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

// 2. 获得工作线程的 Looper & 维护自己的工作队列
mServiceLooper = thread.getLooper();

// 3. 新建mServiceHandler & 绑定上述获得Looper
// 新建的Handler 属于工作线程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}


/**
* 分析1:ServiceHandler源码分析
**/
private final class ServiceHandler extends Handler {
// 构造函数
public ServiceHandler(Looper looper) {
super(looper);
}

// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行
// onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务
stopSelf(msg.arg1);
}

/**
* 分析2: onHandleIntent()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);

2. IntentService 如何通过 onStartCommand 将 Intent 传递给服务 & 依次插入到工作队列中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** 
* onStartCommand()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
public int onStartCommand(Intent intent, int flags, int startId) {

// 调用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {

// 1. 获得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;

// 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
//这里的Intent = 启动服务时startService(Intent) 里传入的 Intent
msg.obj = intent;

// 3. 发送消息,即 添加到消息队列里
mServiceHandler.sendMessage(msg);
}

3. 总结

从上面源码可看出:IntentService本质 = Handler + HandlerThread

  1. 通过HandlerThread 单独开启1个工作线程:IntentService
  2. 创建1个内部 HandlerServiceHandler
  3. 绑定 ServiceHandlerIntentService
  4. 通过 onStartCommand() 传递服务intentServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
  5. 通过onHandleIntent() 依次处理所有Intent对象所对应的任务

因此我们通过复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作 即可

三. 注意事项

1. 工作任务队列 = 顺序执行

即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行。

若服务停止,则会清除消息队列中的消息,后续的事件不执行。

原因:

  1. 由于onCreate()只会调用一次 = 只会创建1个工作线程;
  2. 当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中 & 等待执行。
  3. 所以,多次启动 IntentService 会按顺序执行事件

2. 不建议通过 bindService() 启动 IntentService

1
2
3
4
5
// 在IntentService中,onBind()`默认返回null
@Override
public IBinder onBind(Intent intent) {
return null;
}

采用 bindService()启动 IntentService的生命周期如下:onCreate() ->> onBind() ->> onunbind()->> onDestory()。

即,并不会调用onStart()onStartcommand(),故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作。

四. 实际小例子

1. 自定义一个IntentService,并注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class MyIntentService extends IntentService {
//日志
public static final String TAG = MyIntentService.class.getSimpleName();

/**
* 在构造函数中传入线程名称
*/
public MyIntentService() {
super("MyIntentService");
}

public MyIntentService(String name) {
super(name);
}

/**
* 根据 Intent 实现耗时操作
* @param intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//根据 Intent 的不同,进行不同的事务处理
String task_name = intent.getExtras().getString("task_name");

switch (task_name){
case "task_1":
Log.d(TAG,"正在执行任务 1");
break;

case "task_2":
Log.d(TAG,"正在执行任务 2");
break;
}
}

@Override
public void onCreate() {
super.onCreate();

Log.d(TAG,"onCreate");
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand");

return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();

Log.d(TAG,"onDestroy");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="swu.xl.intentservice">

<application
android:allowBackup="true"
android:icon="@mipmap/view"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!--注册服务-->
<service android:name=".MyIntentService"/>

</application>

</manifest>

2. 在Activity中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//同一个服务只会开启一个线程
//在onHandleIntent方法中,依次处理传入的Intent请求

//请求1
Intent intent = new Intent(this,MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("task_name","task_1");
intent.putExtras(bundle);
startService(intent);

//请求2
intent = new Intent(this,MyIntentService.class);
bundle = new Bundle();
bundle.putString("task_name","task_2");
intent.putExtras(bundle);
startService(intent);

}
}

3. 运行效果

1
2
3
4
5
6
2020-06-03 17:18:58.952 28217-28217/swu.xl.intentservice D/MyIntentService: onCreate
2020-06-03 17:18:58.952 28217-28217/swu.xl.intentservice D/MyIntentService: onStartCommand
2020-06-03 17:18:58.953 28217-28259/swu.xl.intentservice D/MyIntentService: 正在执行任务 1
2020-06-03 17:18:58.953 28217-28217/swu.xl.intentservice D/MyIntentService: onStartCommand
2020-06-03 17:18:58.954 28217-28259/swu.xl.intentservice D/MyIntentService: 正在执行任务 2
2020-06-03 17:18:59.181 28217-28217/swu.xl.intentservice D/MyIntentService: onDestroy

参考文章

Android多线程:这是一份全面 & 详细的IntentService源码分析指南