這幾天面試的時候,反復被問到一個關於Service的問題。
之前做了一個APP。有一個應用場景是,需要開機啟動一個Service,在Service中另開一個線程,去對比用戶配置中的時間,作出及時提醒。
然后面試的時候在描述該做法時就被問到一個問題,如果Service被系統或者其他應用kill了怎么辦?我當時的回答是,在onDestroy中去處理。面試官說,onDestroy並不會被調用。
面試的詳情暫且不表,在后期會專門寫面經。現在討論這個問題,Service被kill后生命周期是怎樣的。
OK,用代碼說話。
1,新建一個項目,項目中有一個Activity,一個Service。在Activity的button的監聽處理中去開啟這個Service
MainActivity.java
package com.zhenghuiy.killedservicelifecycletest; import android.os.Bundle; import android.app.Activity; import android.content.Intent; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener{ private Button startServiceBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } private void initViews() { startServiceBtn = (Button) findViewById(R.id.startService); startServiceBtn.setOnClickListener(this); } @Override public void onClick(View view) { if(view.getId() == R.id.startService){ Intent intent = new Intent(); intent.setClass(this, MyService.class); this.startService(intent); } } }
2,重寫Service的大部分函數,具體看注釋
MyService.java
package com.zhenghuiy.killedservicelifecycletest; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class MyService extends Service implements Runnable{ /* * Service當以bindService的形式調用時,會調用onBind * 當以startService,則調用onStartCommand * 另外,onBind是一個抽象函數,必須重寫 * */ @Override public int onStartCommand(Intent intent, int flags, int startId) { showLog("onStartCommand is called"); //Service運行在UI主線程,為了避免因堵塞而被關閉,另開一個線程 new Thread(this).start(); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent itent) { showLog("onBind is called"); return null; } @Override public void onCreate() { super.onCreate(); showLog("onCreate is called"); } @Override public void onDestroy() { super.onDestroy(); showLog("onDestroy is called"); } /* * onStart方法已經過時 * 在2.0之后的版本使用onStartCommand * */ @Override @Deprecated public void onStart(Intent intent, int startId) { super.onStart(intent, startId); showLog("onStart is called,the Intent action is"+intent.getAction()); } @Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); showLog("onTaskRemoved is called,the Intent action is"+rootIntent.getAction()); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); showLog("onTrimMemory is called,the level is"+level); } private void showLog(String text){ Log.v(this.getClass().getName(),text); } @Override public void run() { while(true){} } }
3,用真機測試
運行后點擊button,啟動service,此時以下函數被調用:
點擊home回到手機桌面,此時該Service仍然在后台運行。onTrimMemory被調用
我使用的手機是華為3C。進入系統的setting后,可以看到顯示該應用有一個進程和一個服務在運行中。
在設置里的應用管理那點擊“停止”。onDestroy被調用
說明,當服務被系統自動或手動(人為的在設置里停止)停止時,仍然會正常走完其生命周期。
4,測試使用其他應用,比如“騰訊手機管家”去停止Service.
前面同樣的過程就不贅述,當Service在后台運行的時候,使用手機管家去“一鍵加速”。
可以在設置——》應用管理 里看到,原來該測試應用的item顯示“有1個進程和1個服務在運行”變成“有0個進程和1個服務在運行”。再刷新一遍,就發現,該應用已經不在運行中的列表里了。
並且,logcat里始終沒有打印“onDestroy is called”.
結論是,其他“管家式”應用“清理”的方法是,直接kill該進程。此時,Service不會走正常的生命周期,也就是onDestroy未被調用。
5,回到問題本身
當時面試官問Service的onDestory並不會被調用,此時你要如何解決。我的回答是:
一種方法是,使用服務器進行推送。如果客戶端有響應,說明Service存活。如果沒有響應,就啟動Service.
另一種方法是,將該Service獨立出來,運行在另一個進程中。(但是仔細想想,這個方法並不能避免Service被kill,因此不算正確答案)。
不知道其他方法還有什么?