一蹲诀、Service 常見問題
由于Service
比較簡單岸晦,就不介紹基礎(chǔ)的東西了禁悠,這篇文章主要是用于記錄使用Service
過程中遇到的一些問題念祭。
1.1 描述 Service 的生命周期
Service
的生命周期如下圖所示:
Service
的啟動方式有兩種,startService
和bindService
碍侦,先 單獨 地看一下這兩種方式的調(diào)用粱坤。
-
startService
:如果該Service
沒有運行,那么會先調(diào)用onCreate
方法瓷产,之后再回調(diào)onStartCommand
方法比规,如果再次啟動已經(jīng)在運行的Service
,仍然會回調(diào)該方法拦英。如果調(diào)用了stopService
,那么會回調(diào)onDestroy
方法测秸。 -
bindService
:如果該Service
沒有運行疤估,那么會先調(diào)用onCreate
方法灾常,之后再回調(diào)onBind
方法,如果再次啟動已經(jīng)在運行的Service
铃拇,仍然會回調(diào)钞瀑。如果調(diào)用了unBindService
,那么會回調(diào)onBind
和onDestroy
方法慷荔。
假如同時通過start
和bind
方式啟動了Service
雕什,那么必須保證stopService
和unBindService
都調(diào)用后,Service
才會被銷毀显晶。
1.2 onStartCommand 返回值的含義
onStartCommand
決定了Service
被系統(tǒng)殺死后的處理行為贷岸。
START_NOT_STICKY
如果Service
在onStartCommand
返回之后被殺死,它不會重啟磷雇。
START_STICKY
如果Service
在onStartCommand
返回之后被殺死偿警,并在稍后 嘗試重新創(chuàng)建 這個Service
,依次回調(diào)onCreate
唯笙、onStartCommand
螟蒸,onStartCommand
當中傳入的Intent
將為null
。
START_REDELIVER_INTENT
如果Service
在onStartCommand
返回之后被殺死崩掘,那么系統(tǒng)會重新創(chuàng)建這個Service
七嫌,依次回調(diào)onCreate
、onStartCommand
苞慢,onStartCommand
當中的Intent
為最后一次傳遞的Intent
诵原。
1.3 同一進程通過 Service 進行通信
前面我們有談到Service
啟動的兩種方式,下面我們介紹一下在同一進程中枉疼,如何和Service
進行交互皮假。
1.3.1 startService 下的交互
通過startService
啟動服務(wù)的時候,只能通過onStartCommand
當中的Intent
進行交互骂维,Service
根據(jù)Intent
中的action
區(qū)分行為惹资,intent
的數(shù)據(jù)作為輸入?yún)?shù)。
這種方式的優(yōu)點是 簡單航闺,缺點是 這種通信方式是單向的褪测,只能由調(diào)用者告訴Service
做什么,Service
無法返回給調(diào)用者信息潦刃。
/**
* @author lizejun
**/
public class CommandWorkerService extends Service {
private static final String TAG = CommandWorkerService.class.getSimpleName();
public static final String ACTION_LOG = "com.android.action.log";
public static final String ACTION_KEY_LOG_MSG = "com.android.action.log.msg";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (!TextUtils.isEmpty(action)) {
switch (action) {
case ACTION_LOG:
Log.d(TAG, intent.getStringExtra(ACTION_KEY_LOG_MSG));
break;
default:
break;
}
}
}
}
調(diào)用者的處理方式:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService();
}
private void startService() {
Intent intent = new Intent(this, CommandWorkerService.class);
intent.setAction(CommandWorkerService.ACTION_LOG);
intent.putExtra(CommandWorkerService.ACTION_KEY_LOG_MSG, "Call CommandWorkerService");
startService(intent);
}
private void stopService() {
Intent intent = new Intent(this, CommandWorkerService.class);
stopService(intent);
}
}
1.3.2 bindService 下的交互
首先定義調(diào)用者和Service
之間交互的接口侮措。
/**
* 契約類,定義和 Service 之間交互的接口乖杠。
*
* @author lizejun
**/
public interface IBindWorker {
/**
* 調(diào)用方法分扎。
*/
int add(int a, int b);
}
實現(xiàn)Service
,在onBind
方法中胧洒,返回一個Binder
的子類畏吓,同時實現(xiàn)了契約類的接口墨状。
/**
* @author lizejun
**/
public class BindWorkerService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new BindWorker();
}
private class BindWorker extends Binder implements IBindWorker {
@Override
public int add(int a, int b) {
return a + b;
}
}
}
實現(xiàn)調(diào)用者,在bindService
方法調(diào)用時菲饼,會要求傳入ServiceConnection
的子類肾砂,在該子類的onServiceConnected
會返回一個IBinder
,這個就是與Service
交互的橋梁宏悦,將它轉(zhuǎn)型為契約類镐确,通過它來調(diào)用相應(yīng)的接口。
public class MainActivity extends AppCompatActivity {
private ServiceConnection mConnection;
private IBindWorker mWorker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mWorker != null) {
//調(diào)用接口方法饼煞。
Log.d("IBindWorker", "result=" + mWorker.add(1, 2));
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unBindService();
}
private void bindService() {
Intent intent = new Intent(this, BindWorkerService.class);
mConnection = new BindWorkerConnection();
bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
}
private void unBindService() {
unbindService(mConnection);
}
private class BindWorkerConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通過返回的 IBinder 子類源葫,與 Service 進行交互。
mWorker = (IBindWorker) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
1.4 不同進程通過 Service 進行通信
在不同的進程之間進行通信派哲,需要采用AIDL
來實現(xiàn)臼氨,這個在 Framework 源碼解析知識梳理(3) - 應(yīng)用進程之間的通信實現(xiàn) 中有詳細的介紹。
1.5 Service 和 Thread 對比
1.5.1 定義區(qū)別
-
Thread
是線程執(zhí)行的最小單元芭届,也是分配CPU
的基本單位储矩。 -
Service
是Android
的四大組件之一,通過Binder
實現(xiàn)沒有UI
的后臺服務(wù)褂乍。
1.5.2 使用場景
- 由于
Android
中不允許在主線程執(zhí)行耗時的操作持隧,因此常通過Thread
將耗時的任務(wù)放在子線程中進行。 -
Service
默認是運行于主線程中的逃片,因此不可以在其中執(zhí)行耗時的操作屡拨,否則會產(chǎn)生ANR
臊恋,它適合于長時間運行在后臺承耿,且不需要交互的場景。
1.6 前臺服務(wù)
前臺服務(wù)相比于普通的后臺Service
有更高的優(yōu)先級里烦,因此在內(nèi)存不足時损离,也不會考慮將其終止哥艇。前臺服務(wù)要求提供通知欄。
-
startForeground(int id, Notification notification)
:將當前服務(wù)設(shè)為前臺服務(wù)僻澎,id
參數(shù)表示唯一標識通知的整數(shù)貌踏,不允許為0
,Notification
是一個通知欄的通知窟勃。 -
stopForeground(boolean removeNotification)
從前臺刪除服務(wù)祖乳,boolean
表示是否也刪除狀態(tài)欄通知,該方法并不會停止服務(wù)秉氧。
1.7 Android 5.0 之后不允許隱式啟動服務(wù)
在Android 5.0
之后眷昆,啟動服務(wù)的時候必須要提供component
或者package
,否則會拋出異常。
1.8 IntentService
IntentService
繼承于Service
隙赁,因此我們可以像start
普通Service
那樣啟動它垦藏。
它常被用于在 后臺執(zhí)行單次的耗時任務(wù),任務(wù)的執(zhí)行邏輯放在onHandleIntent
方法當中伞访。它內(nèi)部是采用HandlerThread
來實現(xiàn)的,因此如果有多個任務(wù)轰驳,那么將會排隊等待執(zhí)行厚掷。
在onHandleIntent
執(zhí)行完后,會調(diào)用stopSelf(int startId)
级解。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
這個方法和stopSelf()
的區(qū)別在于冒黑,stopSelf(int startId) 在只有最后依次啟動服務(wù)的 ID 與它相同時,才會停止服務(wù)勤哗,也就是說抡爹,當最后一次發(fā)送給IntentService
的任務(wù)被執(zhí)行完后,該服務(wù)會自動停止芒划。