2011年9月17日土曜日

Android アプリの終了処理:Low BatteryとShutdown(2)

前回の続きで、こんどはACTION_SHUTDOWNについてです。

前回のエントリのサンプルコードで、LowBatteryの場合と、電源キー押しでのShutdownの場合に、ACTION_SHUTDOWNのBroadcast Intentが投げられることがわかりました。
これを拾って、終了処理をすればよいわけですが、ふと疑問に思います。

「終了処理で、無限ループに陥ったら、どうなるの?」

前回のサンプルコードで、onShutdown()に無限ループのコードを入れたところ・・・端末は正常に?終了しました。
onStop()/onDestroy()にも入れてみましたが、同じ動作です。

なんだか怪しいので、こちらを参考に、Shutdown処理を追ってみました。

ShutdownThread.javaの213行目(Gingerbread 12/23版の場合)で、Broadcast Intentを発行しています。
以下、抜粋。
  1. Log.i(TAG, "Sending shutdown broadcast...");  
  2.           
  3.         // First send the high-level shut down broadcast.  
  4.         mActionDone = false;  
  5.         mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,  
  6.                 br, mHandler, 0nullnull);  
  7.           
  8.         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;  
  9.         synchronized (mActionDoneSync) {  
  10.             while (!mActionDone) {  
  11.                 long delay = endTime - SystemClock.elapsedRealtime();  
  12.                 if (delay <= 0) {  
  13.                     Log.w(TAG, "Shutdown broadcast timed out");  
  14.                     break;  
  15.                 }  
  16.                 try {  
  17.                     mActionDoneSync.wait(delay);  
  18.                 } catch (InterruptedException e) {  
  19.                 }  
  20.             }  
  21.         }  
  22.           
  23.         Log.i(TAG, "Shutting down activity manager...");  
  24.           
  25.         final IActivityManager am =  
  26.             ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));  
  27.         if (am != null) {  
  28.             try {  
  29.                 am.shutdown(MAX_BROADCAST_TIME);  
  30.             } catch (RemoteException e) {  
  31.             }  
  32.         }  
mActionDonwSync.wait()は、ACTION_SHUTDOWNのBroadcast Intent発行が終わると戻ってきますので、発行しただけでam.shutdown()に入ります。

IActivityManagerはリモートインターフェースですが、リモートの先はframeworks/base/services/java/com/android/server/am/ActivityManagerService.javaです。

ActivityManagerService::shutdown()はどのようになっているかというと。
  1. public boolean shutdown(int timeout) {  
  2.         if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)  
  3.                 != PackageManager.PERMISSION_GRANTED) {  
  4.             throw new SecurityException("Requires permission "  
  5.                     + android.Manifest.permission.SHUTDOWN);  
  6.         }  
  7.           
  8.         boolean timedout = false;  
  9.           
  10.         synchronized(this) {  
  11.             mShuttingDown = true;  
  12.             mWindowManager.setEventDispatching(false);  
  13.   
  14.             if (mMainStack.mResumedActivity != null) {  
  15.                 mMainStack.pauseIfSleepingLocked();  
  16.                 final long endTime = System.currentTimeMillis() + timeout;  
  17.                 while (mMainStack.mResumedActivity != null  
  18.                         || mMainStack.mPausingActivity != null) {  
  19.                     long delay = endTime - System.currentTimeMillis();  
  20.                     if (delay <= 0) {  
  21.                         Slog.w(TAG, "Activity manager shutdown timed out");  
  22.                         timedout = true;  
  23.                         break;  
  24.                     }  
  25.                     try {  
  26.                         this.wait();  
  27.                     } catch (InterruptedException e) {  
  28.                     }  
  29.                 }  
  30.             }  
  31.         }  
  32.           
  33.         mUsageStatsService.shutdown();  
  34.         mBatteryStatsService.shutdown();  
  35.           
  36.         return timedout;  
  37.     }  
これ以上追ってないので、推測が入りますが、Activityの終了を待ってはいるけど、timeout引数で指定されてきた時間が過ぎると、Activityの終了をまたなくなる、あきらめちゃう、ということになると思います。

あきらめた後は、、、System系のServiceの停止や、ファイルシステムのアンマウントが始まるようなので、終了できなかったアプリは、動作が不安定になって最終的にKillされちゃうだけかなと。

なお、このTimeoutは、ShutdownThread.javaのMAX_BROADCAST_TIMEで指定されている時間で、Android Originalソースだと10秒になっていました。

MAX_BROADCAST_TIMEの10秒、というのは、単一のアプリからするとかなり長いですが、ほかにShutdown、終了処理をするアプリもいる訳なので、あまり悠長に処理をしてられる余裕もないのかも。

Shutdownで処理をする場合は、間に合わなくなる可能性も考えて、重要なデータから退避するとか、そもそもKillされる前提で設計した方がいいのかな、と思います。

・・・少し考えすぎているような気もしますがそーいうこともあるのかな、と、頭の片隅に入れておきます。

0 件のコメント:

コメントを投稿