Android Intent Service
Learning from the
- Android Official Documentation
- Blog.csdn.net/iromkoear/a…
Overview
IntentService is a subclass of Service that is used to handle asynchronous (IntentService has a worker thread) requests on demand (as indicated by the name of the class). The Service is also very easy to use. It automatically stops when the work is done, so we don’t need to stop it manually.
Using IntentService
The following is a simple example of updating the progress bar with IntentService:
- The Activity registers a temporary broadcast receiver to receive messages and then update the UI
- Click Button to start IntentService and start adding progress
- When progress is added, notify the UI of the change by sending a broadcast
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(a) {
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)
}
}
Copy the code
The Activity of the code
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(a) {
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"}}Copy the code
Enable IntentService multiple times
When we try to click the Button several times in a row (say three times), we will find that the progress bar is full after a few moments, and then start again until we reach the third position. The printed Log is as follows:
E/TAG: onHandleIntent
E/TAG: onHandleIntent
E/TAG: onHandleIntent
Copy the code
The onHandleIntent method is executed multiple times if the Service is started multiple times. This is not the case in the following source code.
Do not use Bind to start the service
When we use the Service, in order to interact and Service, we will through the way of Bind open Service access to communicate with the Service of the Binder, but the way Bind open Service does not apply to IntentService let’s investigate.
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() {}/ /...
}
Copy the code
Binding service
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)
}
/ /...
}
Copy the code
After we BindService, view the Log
E/TAG: onBind
Copy the code
If the onHandleIntent Log is not printed, IntentService is a normal Service without IntentService features. From this we can conclude that you do not need to use Bind to start IntentService.
IntentService source code parsing
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper; //looper
private volatile ServiceHandler mServiceHandler; //handler
private String mName; // The thread name
private boolean mRedelivery;
/** This Handler is a child thread Handler, not a UI communication Handler */
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
/** onHandleIntent */
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
// End the service when all messages have been processedstopSelf(msg.arg1); }}public IntentService(String name) {
super(a); mName = name; }public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate(a) {
super.onCreate();
// instantiate HandlerThread
//HandlerThread descends from Thread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/** As we all know, if a Service is started multiple times, the onStart method will execute IntentService multiple times before onStart to find the one that keeps sending messages to the 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(a) {
mServiceLooper.quit();
}
/** the onBind method was overridden to return null */
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
Copy the code
The above code is very simple, just a layer of encapsulation of the Service, the general flow is as follows:
- When a service is created, IntentService initialization (onCreate) is performed to instantiate HandlerThread
- OnCreate is called immediately after the onCreate method is executed
onStart
Method, which is like sending a message to the Handler - Handler will queue the execution
- When all messages have been processed, the service is terminated
Why can’t you use Bind to start IntentService
By looking at the source code, I think we have found the answer. The onStart lifecycle method is not executed when starting a Service using the Bind method. So while the Bind Service will execute the onCreate method to instantiate the HandlerThread, it’s the onStart method that sends data to the Handler.