2011年8月3日水曜日

Serviceの強制終了

(2011/11/22 こちらのエントリーにじゃあどーすんのよ?について、やってみたことを書いてみました)

Serviceは、Androidシステムによって、終了させられてしまうことがあります。
bindしているActivityが全ていなくなったとか、Low Memoryになったとか。

Serviceには、onDestroy()も、onLowMemory()も存在してますので、
終了させられても、この2つをちゃんと実装しておけば良い・・・

と思いきや、

そんなの呼ばれずにKillされてしまうシチュエーションがあります。
ちょっとググれば、LowMemoryでは、そうなってしまうことがあるようなことがわかりますが、それだけではありません。

だれもそのServiceにbindしてない状態ですと、LowMemoryとか関係なく、一定時間経つとKillされます。
定期的に処理を行っていたとしても、本当に、Killされちゃいます。
onDestroyとか一切呼ばれません。

ServiceがKillされちゃうあたりで、"No longer want xx.service (pid xxxxx): hidden #xx"といったLogが出てきますが、このLogがあるのがframework/base/services/ActivityManagerService.java です。

コードを覗いてみると(12/24版のGingerBread)、
12170行目に、このLogをだしてます。そのちょっと下、12175行目で、やはり、います。
ProcessのKillが。

  1. if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {  
  2.   if (!app.killedBackground) {  
  3.     numHidden++;  
  4.     if (numHidden > MAX_HIDDEN_APPS) {  
  5.       Slog.i(TAG, "No longer want " + app.processName  
  6.                + " (pid " + app.pid + "): hidden #" + numHidden);  
  7.       EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,  
  8.                app.processName, app.setAdj, "too many background");  
  9.       app.killedBackground = true;  
  10.       Process.killProcessQuiet(app.pid);  
  11.     }  
  12.   }  
  13. }  
これでは、onDestroyも呼ばれないのも納得・・・
この動作を避けるためには、Service::startForeground()を、必要なタイミングで呼べばいいです。
実際、ActivityManagerServiceでも、Foreground ServiceはKillしないようにしてます(※)

(※)正確には、Killされにくくなる、みたいです。どれから順番にKillするかはcomputeOomAdjLocked()を読めば良さそう。

  1. Notification notification = new Notification(R.drawable.icon,  
  2. getString(R.string.StartNotification),  
  3. System.currentTimeMillis());  
  4.   
  5. Intent t_intent = new Intent(this, Target.class);  
  6.   
  7. t_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);  
  8. PendingIntent intent = PendingIntent.getActivity(this0,  
  9. t_intent,0);  
  10. notification.setLatestEventInfo(this,  
  11. getString(R.string.app_name),  
  12. getString(R.string.Notification), intent);  
  13. notification.flags = Notification.FLAG_ONGOING_EVENT;  
  14.   
  15. startForeground(FORGROUND_ID, notification);  
Notificationが必要なので、それを作って、startForeground()を呼んであげます。startForeground()の第1引数はNotificationに渡す識別用のIDです。


これで、たいていのシチュエーションではKillされないと思います。
が、ServiceのAPI仕様書にもありますが、Critical な Low Memory状態だと、今画面に出てるActivityから
使われてても、ServiceがKillされるようなこともあるらしいので(気にしなくていいらしいけど)、
Killされる可能性があるというのは、頭の片隅においてServiceを設計したほうが良さそうです。

0 件のコメント:

コメントを投稿