Android四大组件之Service
一、Service的含義
????????Android四大組件之一:Service是一個可以在后臺執(zhí)行長時間運(yùn)行操作而不提供用戶界面的應(yīng)用組件;
????????服務(wù)是Android中實(shí)現(xiàn)程序后臺的解決方案,不依賴任何用戶界面,即使程序被切換到后臺,或者用戶打開了另外一個應(yīng)用程序,服務(wù)還能保持運(yùn)行,服務(wù)非常適合那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)。
????????服務(wù)像Activity那樣也是默認(rèn)運(yùn)行在主線程中,如果有耗時任務(wù)還是要在服務(wù)內(nèi)部創(chuàng)建子線程,不然程序會ANR。(默認(rèn)是耗時操作超過20秒就會報ANR,這個待會可以驗(yàn)證)
二、Service的分類
Service分為本地服務(wù)(LocalService)和遠(yuǎn)程服務(wù)(RemoteService):
1、本地服務(wù)依附在主進(jìn)程上而不是獨(dú)立的進(jìn)程,這樣在一定程度上節(jié)約了資源,另外Local服務(wù)因?yàn)槭窃谕贿M(jìn)程因此不需要IPC,也不需要AIDL。相對于bindService會方便很多。主進(jìn)程被Kill后,服務(wù)便會終止。
2、遠(yuǎn)程服務(wù)為獨(dú)立的進(jìn)程,對應(yīng)進(jìn)程名格式為所在包名加上你指定的android:process字符串。由于是獨(dú)立的進(jìn)程,因此在Activity所在進(jìn)程被Kill的時候,該服務(wù)依然在運(yùn)行,不受其他進(jìn)程影響,有利于為多個進(jìn)程提供服務(wù)具有較高的靈活性。該服務(wù)是獨(dú)立的進(jìn)程,會占用一定資源,并且使用AIDL進(jìn)行IPC稍微麻煩一點(diǎn)。
三、Service的使用場景
1、用于處理網(wǎng)絡(luò)事務(wù)(下載文件)
2、播放音樂(音樂播放器)
3、執(zhí)行文件I/O(讀寫文件)
四、Service的啟動方式
1、startService 啟動的服務(wù):
主要用于啟動一個服務(wù)執(zhí)行后臺任務(wù),不進(jìn)行通信。停止服務(wù)需要主動使用stopService;
2、bindService 啟動的服務(wù):
該方法啟動的服務(wù)可以進(jìn)行通信。停止服務(wù)使用unbindService;
3、startService 同時也 bindService 啟動的服務(wù):
停止服務(wù)應(yīng)同時使用stopService與unbindService;
五、代碼驗(yàn)證
1、驗(yàn)證ANR的問題:
public class MyService extends Service {//創(chuàng)建一個普通服務(wù)private static final String TAG = MyService.class.getSimpleName();@Overridepublic void onCreate() {super.onCreate();Log.i("Kathy", "onCreate - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId() + ", data = " + intent.getStringExtra("data"));try {Thread.sleep(30000);} catch (InterruptedException e) {e.printStackTrace();}return super.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent) {Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId());return null;}@Overridepublic void onDestroy() {Log.i("Kathy", "onDestroy - Thread ID = " + Thread.currentThread().getId());super.onDestroy();} }如果沒有在清單文件中注冊,服務(wù)啟動不起來的。
使用:一個啟動按鈕,一個停止按鈕(服務(wù)是可以不用停止的,不主動停止就會跟著主進(jìn)程的銷毀而銷毀)
public class ServiceDemoActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_service_demo);findViewById(R.id.startService).setOnClickListener(v -> {Intent intent = new Intent(this, MyService.class);intent.putExtra("data", "Kathy");startService(intent);});findViewById(R.id.stopService).setOnClickListener(v -> {Intent intent = new Intent(this, MyService.class);stopService(intent);});} }?結(jié)果:因?yàn)槲以趕ervice的onStartCommand處理了20秒以上的耗時操作(The application may be doing too much work on its main thread)。
解決:加了一個異步線程處理耗時操作
@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId() + ", data = " + intent.getStringExtra("data"));new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(30000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();return super.onStartCommand(intent, flags, startId);}?六、兩種啟動方式和生命周期分析
1、方式一:startService啟動服務(wù)
? ? ? ? 這種方式通常一旦啟動,服務(wù)即可在后臺無限期運(yùn)行,即使啟動服務(wù)的組件已被銷毀也不受影響,已啟動的服務(wù)通常是執(zhí)行單一操作,而且不會將結(jié)果返回給調(diào)用方。(通俗的說就是邏輯控制硬編碼在Service里了,不能用別的應(yīng)用組件控制),如果系統(tǒng)把服務(wù)殺了也就結(jié)束了。
? ? ? ? 針對同一個服務(wù)(全局的服務(wù)),可以在主線程的任何Context的地方啟動,只有第一次啟動需要走onCreate生命周期,只要沒stopService,后續(xù)的啟動不會走這個方法了。
onCreate()-->onStartCommand()-->onDestroy()
第一次啟動:onCreate()-->onStartCommand()
再次啟動:onStartCommand()
停止:onDestroy()
再重新啟動:onCreate()-->onStartCommand()
上面啟動方式驗(yàn)證:
上面的ServiceDemoActivity的startService按一次,stopService按一次:
?上面的ServiceDemoActivity的startService按兩次,stopService按一次:
?可以看出:onStartCommand()方法很重要,我們在該方法中根據(jù)傳入的Intent參數(shù)進(jìn)行實(shí)際的操作,比如會在此處創(chuàng)建一個線程用于下載數(shù)據(jù)或播放音樂等。
注意一個點(diǎn)就是onStartCommand的返回值:
START_NOT_STICKY:表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,不會重新創(chuàng)建該Service,如果想重新實(shí)例化該Service,就必須重新調(diào)用startService來啟動。
使用場景:表示當(dāng)Service在執(zhí)行工作中被中斷幾次無關(guān)緊要或者對Android內(nèi)存緊張的情況下需要被殺掉且不會立即重新創(chuàng)建這種行為也可接受的話,這是可以在onStartCommand()返回值中設(shè)置該值。例如在Service中定時從服務(wù)器中獲取最新數(shù)據(jù)。
START_STICKY:表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,Android系統(tǒng)會將該Service依然設(shè)置為started狀態(tài)(即運(yùn)行狀態(tài)),但是不再保存onStartCommand方法傳入的intent對象,然后Android系統(tǒng)會嘗試再次重新創(chuàng)建該Service,并執(zhí)行onStartCommand()回調(diào)方法,這時onStartCommand()回調(diào)方法的Intent參數(shù)為null,也就是onStartCommand()方法雖然會執(zhí)行但是獲取不到intent信息。
使用場景:如果你的Service可以在任意時刻運(yùn)行或結(jié)束都沒什么問題,而且不需要intent信息,那么就可以在onStartCommand()方法中返回START_STICKY,比如一個用來播放背景音樂功能的Service就適合返回該值。
START_REDELIVER_INTENT:表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,與返回START_STICKY的情況類似,Android系統(tǒng)會將再次重新創(chuàng)建該Service,并執(zhí)行onStartCommand()回調(diào)方法,但是不同的是,Android系統(tǒng)會再次將Service在被殺掉之前最后一次傳入onStartCommand()方法中的Intent再次保留下來并再次傳入到重新創(chuàng)建后的Service的onStartCommand方法中,這樣我們就能讀取到intent參數(shù)。
使用場景:如果我們的Service需要依賴具體的Intent才能運(yùn)行(需要從Intent中讀取相關(guān)數(shù)據(jù)信息等),并且在強(qiáng)制銷毀后有必要重新創(chuàng)建運(yùn)行,那么這樣的Service就適合返回START_REDELIVER_INTENT。
2、方式二:bindService綁定服務(wù)
bindService綁定服務(wù)特點(diǎn):
1.bindService啟動的服務(wù)和調(diào)用者之間是典型的client-server模式。調(diào)用者是client,service則是server端。service只有一個,但綁定到service上面的client可以有一個或很多個。這里所提到的client指的是組件,比如某個Activity。
2.client可以通過IBinder接口獲取Service實(shí)例,從而實(shí)現(xiàn)在client端直接調(diào)用Service中的方法以實(shí)現(xiàn)靈活交互,這在通過startService方法啟動中是無法實(shí)現(xiàn)的。
3.bindService啟動服務(wù)的生命周期與其綁定的client息息相關(guān)。當(dāng)client銷毀時,client會自動與Service解除綁定。當(dāng)然,client也可以明確調(diào)用Context的unbindService()方法與Service解除綁定。當(dāng)沒有任何client與Service綁定時,Service會自行銷毀。
代碼驗(yàn)證:
public class MyBinderService extends Service {private static final String TAG = "Kathy";MyBinder myBinder;@Overridepublic void onCreate() {super.onCreate();myBinder = new MyBinder();Log.i("Kathy", "onCreate - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName());}@Nullable@Overridepublic IBinder onBind(Intent intent) {Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName());return myBinder;}@Overridepublic boolean onUnbind(Intent intent) {Log.i("Kathy", "onUnbind");return super.onUnbind(intent);}@Overridepublic void onDestroy() {Log.i("Kathy", "onDestroy");super.onDestroy();}public class MyBinder extends Binder {public void StartBB() {new Thread(new Runnable() {@Overridepublic void run() {//處理具體的邏輯stopSelf();}}).start();Log.d(TAG, "StartBB:BBBBBBBBBB");}} } public class ServiceDemoActivity2 extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_service_demo2);findViewById(R.id.BinderService).setOnClickListener(v -> {Intent bindIntent = new Intent(ServiceDemoActivity2.this, MyBinderService.class);//這里的三個參數(shù)后面會細(xì)說bindService(bindIntent, mConnection, BIND_AUTO_CREATE);});findViewById(R.id.unBinderService).setOnClickListener(v -> {//多次取消綁定程序會crashunbindService(mConnection);});}private final ServiceConnection mConnection = new ServiceConnection() {//bind服務(wù),onCreate之后@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {MyBinderService.MyBinder binder1 = (MyBinderService.MyBinder) binder;binder1.StartBB();Log.d("Kathy", "onServiceConnected");}//unBind服務(wù)時,在onDestroy之前@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d("Kathy", "onServiceDisconnected");}}; }?按下綁定按鈕先啟動服務(wù):
?再按下解綁按鈕或者返回上一個頁面:
?生命周期也是onCreate-->onBind-->onServiceConnected-->onUnbind-->onDestory
可以看出ServiceConnection在client和service其中起到了紐帶的作用。
總結(jié)
以上是生活随笔為你收集整理的Android四大组件之Service的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net MVC路由
- 下一篇: Cucumber+Rest Assure