Android四大组件之Service
一、Service的含義
????????Android四大組件之一:Service是一個可以在后臺執行長時間運行操作而不提供用戶界面的應用組件;
????????服務是Android中實現程序后臺的解決方案,不依賴任何用戶界面,即使程序被切換到后臺,或者用戶打開了另外一個應用程序,服務還能保持運行,服務非常適合那些不需要和用戶交互而且還要求長期運行的任務。
????????服務像Activity那樣也是默認運行在主線程中,如果有耗時任務還是要在服務內部創建子線程,不然程序會ANR。(默認是耗時操作超過20秒就會報ANR,這個待會可以驗證)
二、Service的分類
Service分為本地服務(LocalService)和遠程服務(RemoteService):
1、本地服務依附在主進程上而不是獨立的進程,這樣在一定程度上節約了資源,另外Local服務因為是在同一進程因此不需要IPC,也不需要AIDL。相對于bindService會方便很多。主進程被Kill后,服務便會終止。
2、遠程服務為獨立的進程,對應進程名格式為所在包名加上你指定的android:process字符串。由于是獨立的進程,因此在Activity所在進程被Kill的時候,該服務依然在運行,不受其他進程影響,有利于為多個進程提供服務具有較高的靈活性。該服務是獨立的進程,會占用一定資源,并且使用AIDL進行IPC稍微麻煩一點。
三、Service的使用場景
1、用于處理網絡事務(下載文件)
2、播放音樂(音樂播放器)
3、執行文件I/O(讀寫文件)
四、Service的啟動方式
1、startService 啟動的服務:
主要用于啟動一個服務執行后臺任務,不進行通信。停止服務需要主動使用stopService;
2、bindService 啟動的服務:
該方法啟動的服務可以進行通信。停止服務使用unbindService;
3、startService 同時也 bindService 啟動的服務:
停止服務應同時使用stopService與unbindService;
五、代碼驗證
1、驗證ANR的問題:
public class MyService extends Service {//創建一個普通服務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();} }如果沒有在清單文件中注冊,服務啟動不起來的。
使用:一個啟動按鈕,一個停止按鈕(服務是可以不用停止的,不主動停止就會跟著主進程的銷毀而銷毀)
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);});} }?結果:因為我在service的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啟動服務
? ? ? ? 這種方式通常一旦啟動,服務即可在后臺無限期運行,即使啟動服務的組件已被銷毀也不受影響,已啟動的服務通常是執行單一操作,而且不會將結果返回給調用方。(通俗的說就是邏輯控制硬編碼在Service里了,不能用別的應用組件控制),如果系統把服務殺了也就結束了。
? ? ? ? 針對同一個服務(全局的服務),可以在主線程的任何Context的地方啟動,只有第一次啟動需要走onCreate生命周期,只要沒stopService,后續的啟動不會走這個方法了。
onCreate()-->onStartCommand()-->onDestroy()
第一次啟動:onCreate()-->onStartCommand()
再次啟動:onStartCommand()
停止:onDestroy()
再重新啟動:onCreate()-->onStartCommand()
上面啟動方式驗證:
上面的ServiceDemoActivity的startService按一次,stopService按一次:
?上面的ServiceDemoActivity的startService按兩次,stopService按一次:
?可以看出:onStartCommand()方法很重要,我們在該方法中根據傳入的Intent參數進行實際的操作,比如會在此處創建一個線程用于下載數據或播放音樂等。
注意一個點就是onStartCommand的返回值:
START_NOT_STICKY:表示當Service運行的進程被Android系統強制殺掉之后,不會重新創建該Service,如果想重新實例化該Service,就必須重新調用startService來啟動。
使用場景:表示當Service在執行工作中被中斷幾次無關緊要或者對Android內存緊張的情況下需要被殺掉且不會立即重新創建這種行為也可接受的話,這是可以在onStartCommand()返回值中設置該值。例如在Service中定時從服務器中獲取最新數據。
START_STICKY:表示Service運行的進程被Android系統強制殺掉之后,Android系統會將該Service依然設置為started狀態(即運行狀態),但是不再保存onStartCommand方法傳入的intent對象,然后Android系統會嘗試再次重新創建該Service,并執行onStartCommand()回調方法,這時onStartCommand()回調方法的Intent參數為null,也就是onStartCommand()方法雖然會執行但是獲取不到intent信息。
使用場景:如果你的Service可以在任意時刻運行或結束都沒什么問題,而且不需要intent信息,那么就可以在onStartCommand()方法中返回START_STICKY,比如一個用來播放背景音樂功能的Service就適合返回該值。
START_REDELIVER_INTENT:表示Service運行的進程被Android系統強制殺掉之后,與返回START_STICKY的情況類似,Android系統會將再次重新創建該Service,并執行onStartCommand()回調方法,但是不同的是,Android系統會再次將Service在被殺掉之前最后一次傳入onStartCommand()方法中的Intent再次保留下來并再次傳入到重新創建后的Service的onStartCommand方法中,這樣我們就能讀取到intent參數。
使用場景:如果我們的Service需要依賴具體的Intent才能運行(需要從Intent中讀取相關數據信息等),并且在強制銷毀后有必要重新創建運行,那么這樣的Service就適合返回START_REDELIVER_INTENT。
2、方式二:bindService綁定服務
bindService綁定服務特點:
1.bindService啟動的服務和調用者之間是典型的client-server模式。調用者是client,service則是server端。service只有一個,但綁定到service上面的client可以有一個或很多個。這里所提到的client指的是組件,比如某個Activity。
2.client可以通過IBinder接口獲取Service實例,從而實現在client端直接調用Service中的方法以實現靈活交互,這在通過startService方法啟動中是無法實現的。
3.bindService啟動服務的生命周期與其綁定的client息息相關。當client銷毀時,client會自動與Service解除綁定。當然,client也可以明確調用Context的unbindService()方法與Service解除綁定。當沒有任何client與Service綁定時,Service會自行銷毀。
代碼驗證:
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);//這里的三個參數后面會細說bindService(bindIntent, mConnection, BIND_AUTO_CREATE);});findViewById(R.id.unBinderService).setOnClickListener(v -> {//多次取消綁定程序會crashunbindService(mConnection);});}private final ServiceConnection mConnection = new ServiceConnection() {//bind服務,onCreate之后@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {MyBinderService.MyBinder binder1 = (MyBinderService.MyBinder) binder;binder1.StartBB();Log.d("Kathy", "onServiceConnected");}//unBind服務時,在onDestroy之前@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d("Kathy", "onServiceDisconnected");}}; }?按下綁定按鈕先啟動服務:
?再按下解綁按鈕或者返回上一個頁面:
?生命周期也是onCreate-->onBind-->onServiceConnected-->onUnbind-->onDestory
可以看出ServiceConnection在client和service其中起到了紐帶的作用。
總結
以上是生活随笔為你收集整理的Android四大组件之Service的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net MVC路由
- 下一篇: Android 四大组件 -- serv