前言
-
多线程的应用在Android开发中是非常常见的,常用方法主要有:
- 继承Thread类
- 实现Runnable接口
- AsyncTask
- Handler
- HandlerThread
- IntentService
-
今天,我将全面解析多线程其中一种常见用法:
IntentService
目录
示意图1. 定义
Android
里的一个封装类,继承四大组件之一的Service
2. 作用
处理异步请求 & 实现多线程
3. 使用场景
线程任务 需 按顺序、在后台执行
- 最常见的场景:离线下载
- 不符合多个数据同时请求的场景:所有的任务都在同一个
Thread looper
里执行
4. 使用步骤
步骤1:定义 IntentService
的子类
需 传入线程名称、复写
onHandleIntent()
方法
步骤2:在Manifest.xml
中注册服务
步骤3:在Activity
中开启Service
服务
5. 实例应用
步骤1:定义 IntentService
的子类
传入线程名称、复写
onHandleIntent()
方法
public class myIntentService extends IntentService {
/**
* 在构造函数中传入线程名字
**/
public myIntentService() {
// 调用父类的构造函数
// 参数 = 工作线程的名字
super("myIntentService");
}
/**
* 复写onHandleIntent()方法
* 根据 Intent实现 耗时任务 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根据 Intent的不同,进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 复写onStartCommand()方法
* 默认实现 = 将请求的Intent添加到工作队列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步骤2:在Manifest.xml中注册服务
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步骤3:在Activity中开启Service服务
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服务只会开启1个工作线程
// 在onHandleIntent()函数里,依次处理传入的Intent请求
// 将请求通过Bundle对象传入到Intent,再传入到服务里
// 请求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 请求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
}
测试结果
运行结果6. 源码分析
-
IntentService
的源码工作流程如下:
特别注意:若启动IntentService
多次,那么 每个耗时操作 则 以队列的方式 在 IntentService
的 onHandleIntent
回调方法中依次执行,执行完自动结束
接下来,我们将通过 源码分析 解决以下问题:
-
IntentService
如何单独开启1个新的工作线程 -
IntentService
如何通过onStartCommand()
将Intent 传递给服务 & 依次插入到工作队列中
问题1:IntentService如何单独开启1个新的工作线程
主要分析内容 = IntentService
源码中的 onCreate()
方法
@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 传递给服务 & 依次插入到工作队列中
/**
* 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);
}
总结
从上面源码可看出:IntentService
本质 = Handler
+ HandlerThread
:
- 通过
HandlerThread
单独开启1个工作线程:IntentService
- 创建1个内部
Handler
:ServiceHandler
- 绑定
ServiceHandler
与IntentService
- 通过
onStartCommand()
传递服务intent
到ServiceHandler
、依次插入Intent
到工作队列中 & 逐个发送给onHandleIntent()
- 通过
onHandleIntent()
依次处理所有Intent
对象所对应的任务
因此我们通过复写
onHandleIntent()
& 在里面 根据Intent
的不同进行不同线程操作 即可
7. 注意事项
注意事项1. 工作任务队列 = 顺序执行
即 若一个任务正在
IntentService
中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行
- 原因:
- 由于
onCreate()
只会调用一次 = 只会创建1个工作线程; - 当多次调用
startService(Intent)
时(即onStartCommand()
也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中 & 等待执行。
3, 所以,多次启动 IntentService 会按顺序执行事件
若服务停止,则会清除消息队列中的消息,后续的事件不执行
注意事项2:不建议通过 bindService() 启动 IntentService
原因:
// 在IntentService中,onBind()`默认返回null
@Override
public IBinder onBind(Intent intent) {
return null;
}
- 采用
bindService()
启动IntentService
的生命周期如下:
onCreate() ->> onBind() ->> onunbind()->> onDestory()
- 即,并不会调用
onStart()
或onStartcommand()
,故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作
此时,你应该使用
Service
,而不是IntentService
8. 对比
8.1 IntentService与Service的区别
示意图8.2 IntentService与其他线程的区别
示意图9. 总结
- 本文主要 全面介绍了 多线程
IntentService
用法 & 源码 - 接下来,我会继续讲解
Android
开发中关于多线程的知识,包括继承Thread
类、实现Runnable
接口、Handler
等等,有兴趣可以继续关注Carson_Ho的安卓开发笔记
请点赞!因为你的鼓励是我写作的最大动力!
相关文章阅读
Android开发:最全面、最易懂的Android屏幕适配解决方案
Android事件分发机制详解:史上最全面、最易懂
Android开发:史上最全的Android消息推送解决方案
Android开发:最全面、最易懂的Webview详解
Android开发:JSON简介及最全面解析方法!
Android四大组件:Service服务史上最全面解析
Android四大组件:BroadcastReceiver史上最全面解析
欢迎关注Carson_Ho的简书!
不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度。
网友评论
public MyIntentService(String name) {
super(name);
}
这个时候minifest.xml里会报继承IntentService的这个类没有默认的构造函数。
需要自己再添加一个参数为空的构造函数才能通过编译,
比如:public MyIntentService(){super("WorkThread");}
05-14 18:41:51.531 2629-2629/? I/myIntentService: onCreate
05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task1
05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task2
05-14 18:41:51.531 2629-2629/? I/myIntentService: onStartCommand task1
05-14 18:41:51.541 2629-2642/? I/myIntentService: do task1
05-14 18:41:51.541 2629-2642/? I/myIntentService: do task2
05-14 18:41:51.541 2629-2642/? I/myIntentService: do task1
05-14 18:41:51.721 2629-2629/? I/myIntentService: onDestroy
这下感觉能和你的文章逻辑匹配上了
ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收,所以不存在内存泄露。
ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收,所以不存在内存泄露。