Android Intent Service
學習自
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 中傳送資料。