Android Intent Service
学习自
- Android 官方文档
- https://blog.csdn.net/iromkoear/article/details/63252665
Overview
IntentService 是Service的子类,他被用来根据需求处理异步(IntentService中存在一个工作线程)请求(表现为Intent,从类的名字也可以看出来)。 而且这个Service非常省心,当工作完成后会自动停止,不需要我们手动停止。
使用IntentService
下面是一个简单的通过IntentService来更新进度条的示例,示例流程如下:
- Activity中祖册临时的广播接受者,来接收消息,然后更新UI
- 点击Button开启IntentService,然后开始增加进度
- 当增加了进度以后,通过发送广播,来通知UI改变
IntentService
class TestIntentService(name: String?) : IntentService(name) {
/**
* 注意必须要有一个无参数的构造函数
* 当前环境使用的API是API26
* IntentService并没有无参数的构造函数
* 所以我们这里需要自己创建一个
* 否则会报错
*
* name 参数 代表的工作线程的命名
* */
constructor() : this("TestIntentService") {
}
companion object {
val ACTION_UPDATE_PROGRESS = "com.shycoder.cn.studyintentservice.UPDATE_PROGRESS"
}
private var progress = 0
private lateinit var mLocalBroadcastManager: LocalBroadcastManager
private var isRunning = true
override fun onCreate() {
super.onCreate()
this.mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
}
override fun onHandleIntent(intent: Intent?) {
Log.e("TAG", "onHandleIntent")
if (intent == null || intent.action != ACTION_UPDATE_PROGRESS) {
return
}
this.isRunning = true
this.progress = 0
while (isRunning) {
SystemClock.sleep(500)
progress += 10
if (progress >= 100) {
this.isRunning = false
}
this.sendBroadcast(if (isRunning) "running" else "finish", progress)
}
}
/**
* send broadcast to activity for notifying change of status and progress
* */
private fun sendBroadcast(status: String, progress: Int) {
val intent = Intent()
intent.action = MainActivity.ACTION_STATUS_CHANGED
intent.putExtra("status", status)
intent.putExtra("progress", progress)
this.mLocalBroadcastManager.sendBroadcast(intent)
}
}
Activity的代码
class MainActivity : AppCompatActivity() {
val mReceiver = TestBroadcastReceiver()
lateinit var mLocalBroadcastManager: LocalBroadcastManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//register broadcast
val intentFilter = IntentFilter(ACTION_STATUS_CHANGED)
this.mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
mLocalBroadcastManager.registerReceiver(mReceiver, intentFilter)
}
/**
* start intent service
* */
fun startIntentService(view: View) {
val intent = Intent(this, TestIntentService::class.java)
intent.action = TestIntentService.ACTION_UPDATE_PROGRESS
this.startService(intent)
}
override fun onDestroy() {
super.onDestroy()
this.unregisterReceiver(this.mReceiver)
}
inner class TestBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) {
return
}
val status = intent.getStringExtra("status")
val progress = intent.getIntExtra("progress", 0)
tvStatus.text = status
progress_barTest.progress = progress
}
}
companion object {
val ACTION_STATUS_CHANGED = "cn.shycoder.studyintentservice.STATUS_CHANGED"
}
}
多次开启IntentService
当我们试着连续点击多次Button(比如说三次), 稍等片刻就会发现进度条满了以后,又重新开始了直到三次位置。打印出来的Log如下:
E/TAG: onHandleIntent
E/TAG: onHandleIntent
E/TAG: onHandleIntent
通过Log和UI的变化,我们可以发现,如果多次开启Service的话,那么 onHandleIntent
方法就会执行多次,这一点与 Service
大相庭径需要我们格外的关注,至于为什么是这种情况,在接下来的源码解析中会提到。
不要使用Bind的方式开启服务
在我们使用 Service
的时候,为了能和Service进行交互,我们会通过Bind的方式开启服务获取与Service进行通信的Binder,但是Bind开启服务的方式并不适用于 IntentService
下面我们来验证一下。
class TestIntentService(name: String?) : IntentService(name) {
/**
* 注意必须要有一个无参数的构造函数
* 当前环境使用的API是API26
* IntentService并没有无参数的构造函数
* 所以我们这里需要自己创建一个
* 否则会报错
*
* name 参数 代表的工作线程的命名
* */
constructor() : this("TestIntentService") {
}
private val mBinder = MyBinder()
override fun onBind(intent: Intent?): IBinder {
Log.e("TAG", "onBind")
return this.mBinder
}
inner class MyBinder : Binder() {
}
//...
}
绑定服务
class MainActivity : AppCompatActivity() {
//....
lateinit var mLocalBroadcastManager: LocalBroadcastManager
lateinit var mService: TestIntentService.MyBinder
private val mConn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = service as TestIntentService.MyBinder
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
/**
* bind intent service
* */
fun bindService(view: View) {
val intent = Intent(this, TestIntentService::class.java)
intent.action = TestIntentService.ACTION_UPDATE_PROGRESS
this.bindService(intent, this.mConn, Context.BIND_AUTO_CREATE)
}
//...
}
当我们BindService后,查看Log
E/TAG: onBind
我们发现 onHandleIntent
方法的Log并没有打印出来,这时候 IntentService
就是一个普通的Service了,而不具备IntentService的特性。由此我们可以得出结论——不用使用Bind的方式开启IntentService。
IntentService源码解析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper; //looper
private volatile ServiceHandler mServiceHandler; //handler
private String mName; //线程的名字
private boolean mRedelivery;
/**
这个Handler是子线程的Handler,并不是与UI通信的Handler
*/
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
/**
调用 onHandleIntent
*/
@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() {
super.onCreate();
//实例化HandlerThread
//HandlerThread继承自Thread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
众所周知,如果多次开启Service的话,那么 onStart方法就会执行多次
IntentService在onStart找那个不断地向Handler发送消息
*/
@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();
}
/**
onBind 方法被重写,返回null
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
上面的代码很简单,仅仅是对Service进行了一层封装,大致流程如下:
- 当创建service的时候,进行IntentService的初始化操作(onCreate),实例化 HandlerThread
- onCreate方法执行以后,紧接着就会调用
onStart
方法,这时候就像向Handler发送消息 - handler 会进行排队执行
- 当所有的消息都处理完成了以后,会将服务结束
为什么不能用Bind的方式开启IntentService
通过查看源码,我想大家已经找到答案了。因为Start方式和Bind的方式开启Service的时候执行的生命周期的方法是不同的,通过Bind的方法开启Service,并不会执行 onStart
生命周期方法。 所以虽然 Bind的方式开启Service会执行onCreate方法来实例化 HandlerThread
但是因为 onStart
方法才向Handler 中发送数据。