1. 程式人生 > >android——徹底關閉——應用程式

android——徹底關閉——應用程式

最近學習做android的遊戲開發時候,發現一個關於android退出時不能徹底關閉的問題,比如:一個程式裡new 出了N多個Thread,這樣在退出程式的可能不能完全關閉,最後發現,只用finish()方法,有時候不能徹底退出,個人感覺還是要在適當的地方加上:System.exit(0);

-=====-=-=-=-=-=======-----=====

1. finish()方法

該方法可以結束當前 Activity,但是如果你的App有很多 Activity 的話,使用該方法顯得有點捉襟見肘了。

另外,還有一個方法finishActivity (int requestCode) ,關於這個方法,先看看sdk的api說明吧!

  1. public void finishActivity (int requestCode)  
  2. Since: API Level 1  
  3. Force finish another activity that you had previously started with startActivityForResult(Intent, int).  
  4. Parameters requestCode  The request code of the activity that you had given to startActivityForResult(). If there are multiple activities started with this request code, they will all be finished.  

也許你會這樣理解 ,Activity1 通過方法 startActivityForResult (Intent, int) 啟動 Activity2,然後在 Activity2 中通過方法finishActivity (int requestCode)來結束 Activity1,但是很不幸運,不是這樣的。不信你可以Demo一把! 

上面文件說得很明白,該方法強制關閉通過方法 startActivityForResult (Intent, int) 啟動的 Activity,也就是說在 Activity1 中的(重寫)方法onActivityResult(int requestCode, int resultCode, Intent data) 來接收 Activity2 返回的結果,必須在 Activity1 中呼叫finishActivity (int requestCode)來結束 Activity2。(一般在onActivityResult 方法呼叫該方法結束 Activity2)。

  1. Force finish another activity that you had previously started with startActivityForResult(Intent, int).  
  2. Parameters  

還有,下面兩個方法,可以參閱文件以及原始碼研究一下。

  1. finishActivityFromChild(Activity child, int requestCode)  
  2. finishFromChild(Activity child)  

2. killProcess

通過呼叫 android.os.Process 的相關方法,結束 App,示例如下:

  1. btn_exit.setOnClickListener(new Button.OnClickListener() {  
  2.     @Override
  3.     publicvoid onClick(View v) {  
  4.         android.os.Process.killProcess(android.os.Process.myPid());  
  5.     }  
  6.       });  

3. exit

我們知道,Java 的 exit(int code) 方法可以退出程式,通過檢視該方法原始碼,知道它實際上是呼叫下面的方法:

  1. Runtime.getRuntime().exit(code);  

示例程式碼,如下所示:

  1. btn_exit.setOnClickListener(new Button.OnClickListener() {  
  2.             @Override
  3.             publicvoid onClick(View v) {  
  4.                 System.exit(0);//正常退出App
  5.             }  
  6.         });  

接下來,我們研究一下這個方法。java.lang.System這個類的該方法jdk說明:

  1. exit  
  2. public static void exit(int status)  
  3. 終止當前正在執行的 Java 虛擬機器。引數用作狀態碼;根據慣例,非 0 的狀態碼錶示異常終止。  
  4. 該方法呼叫 Runtime 類中的 exit 方法。該方法永遠不會正常返回。  
  5. 呼叫 System.exit(n) 實際上等效於呼叫:  
  6.  Runtime.getRuntime().exit(n)  
  7. 引數:  
  8. status - 退出狀態。  
  9. 丟擲:  
  10. SecurityException - 如果安全管理器存在並且其 checkExit 方法不允許以指定狀態退出。  
  11. 另請參見:  
  12. Runtime.exit(int)  

也就是說,引數為非0值的話是異常退出程式。引數為0的話,就是正常退出。

看RunTime這個類的該方法原始碼:

  1. publicvoid exit(int status) {  
  2.         SecurityManager security = System.getSecurityManager();  
  3.     if (security != null) {  
  4.         security.checkExit(status);  
  5.     }  
  6.     Shutdown.exit(status);  
  7. }  

其api說明:

  1. exit  
  2. public void exit(int status)  
  3. 通過啟動虛擬機器的關閉序列,終止當前正在執行的 Java 虛擬機器。此方法從不正常返回。可以將變數作為一個狀態碼;根據慣例,非零的狀態碼錶示非正常終止。  
  4. 虛擬機器的關閉序列包含兩個階段。在第一個階段中,會以某種未指定的順序啟動所有已註冊的關閉鉤子 (hook)(如果有的話),並且允許它們同時執行直至結束。在第二個階段中,如果已啟用退出終結,則執行所有未呼叫的終結方法。一旦完成這個階段,虛擬機器就會暫停。  
  5. 如果在虛擬機器已開始其關閉序列後才呼叫此方法,那麼若正在執行關閉鉤子,則將無限期地阻斷此方法。如果已經執行完關閉鉤子,並且已啟用退出終結 (on-exit finalization),那麼此方法將利用給定的狀態碼(如果狀態碼是非零值)暫停虛擬機器;否則將無限期地阻斷虛擬機器。  
  6. System.exit 方法是呼叫此方法的一種傳統而便捷的方式。  
  7. 引數:  
  8. status - 終止狀態。按照慣例,非零的狀態碼錶明非正常終止。  
  9. 丟擲:  
  10. SecurityException - 如果安全管理器存在,並且其 checkExit 方法不允許存在指定的狀態  
  11. 另請參見:  
  12. SecurityException, SecurityManager.checkExit(int), addShutdownHook(java.lang.Thread), removeShutdownHook(java.lang.Thread), runFinalizersOnExit(boolean), halt(int)  

該方法又是呼叫Shutdown這個類的exit()方法。

  1. staticvoid exit(int status) {  
  2.     boolean runMoreFinalizers = false;  
  3.     synchronized (lock) {  
  4.         if (status != 0) runFinalizersOnExit = false;  
  5.         switch (state) {  
  6.         case RUNNING:   /* Initiate shutdown */
  7.         state = HOOKS;  
  8.         break;  
  9.         case HOOKS:     /* Stall and halt */
  10.         break;  
  11.         case FINALIZERS:  
  12.         if (status != 0) {  
  13.             /* Halt immediately on nonzero status */
  14.             halt(status);  
  15.         } else {  
  16.             /* Compatibility with old behavior: 
  17.              * Run more finalizers and then halt 
  18.              */
  19.             runMoreFinalizers = runFinalizersOnExit;  
  20.         }  
  21.         break;  
  22.         }  
  23.     }  
  24.     if (runMoreFinalizers) {  
  25.         runAllFinalizers();  
  26.         halt(status);  
  27.     }  
  28.     synchronized (Shutdown.class) {  
  29.         /* Synchronize on the class object, causing any other thread 
  30.              * that attempts to initiate shutdown to stall indefinitely 
  31.          */
  32.         sequence();  
  33.         halt(status);  
  34.     }  
  35.     }  

其中,runAllFinalizers()是一個本地方法:

  1. JNIEXPORT void JNICALL  
  2. Java_java_lang_Shutdown_runAllFinalizers(JNIEnv *env, jclass ignored)  
  3. {  
  4.     jclass cl;  
  5.     jmethodID mid;  
  6.     if ((cl = (*env)->FindClass(env, "java/lang/ref/Finalizer"))  
  7.     && (mid = (*env)->GetStaticMethodID(env, cl,  
  8.                         "runAllFinalizers""()V"))) {  
  9.     (*env)->CallStaticVoidMethod(env, cl, mid);  
  10.     }  
  11. }  

System.exit()的引數是把退出原因返回給系統, 一般來說可以是任何的整數 。

0表示正常退出,1表示非正常 。

最後說一下finish()與exit方法的區別:

finish()是Activity的類方法,僅僅針對Activity,當呼叫finish()時,只是將活動推向後臺,並沒有立即釋放記憶體,活動的資源並沒有被清理;當呼叫System.exit(0)時,退出當前Activity並釋放資源(記憶體),但是該方法不可以結束整個App如有多個Activty或者有其他元件service等不會結束。

其實android的機制決定了使用者無法完全退出應用,當你的application最長時間沒有被用過的時候,android自身會決定將application關閉了。


4. restartPackage方法

  1. ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);      
  2. manager.restartPackage(getPackageName());  

首先需要建立ActivityManager物件,然後呼叫restartPackage()方法(如果有興趣的話,可以看原始碼)。

注意:getPackageName獲得當前應用包名稱,如mark.zhang

使用這種方式來退出App,需要許可權:

  1. <uses-permissionandroid:name="android.permission.RESTART_PACKAGES"/>

更加詳細的說明,如下:

  1. void android.app.ActivityManager.restartPackage(String packageName)  
  2. Have the system perform a force stop of everything associated with the given application package. All processes that share its uid will be killed, all services it has running stopped, all activities removed, etc. In addition, a Intent.ACTION_PACKAGE_RESTARTED broadcast will be sent, so that any of its registered alarms can be stopped, notifications removed, etc.  
  3. You must hold the permission android.Manifest.permission.RESTART_PACKAGES to be able to call this method.  
  4. Parameters:  
  5.     packageName The name of the package to be stopped.  

可以看出,相同的UID的程序會被kill,還會停止相關的服務以及移除所有的Activity,並且會發送一個廣播。

注意一個問題:在android2.2之後,該方法不可以將應用程式結束,需要使用ActivityManager類的下面這個方法:

  1. publicvoid killBackgroundProcesses (String packageName)  

api 文件說的很清楚:

  1. public void restartPackage (String packageName)  
  2. Since: API Level 3  
  3. This method is deprecated.  
  4. This is now just a wrapper for killBackgroundProcesses(String); the previous behavior here is no longer available to applications because it allows them to break other applications by removing their alarms, stopping their services, etc.  

另外,需要使用許可權:

  1. <uses-permissionandroid:name="android.permission.KILL_BACKGROUND_PROCESSES"/>

但是不管你怎麼樣折騰,還是無法退出App,嗚呼哀哉!這裡給出一個方法:

  1. int currentVersion = android.os.Build.VERSION.SDK_INT;  
  2.             if (currentVersion > android.os.Build.VERSION_CODES.ECLAIR_MR1) {  
  3.                 Intent startMain = new Intent(Intent.ACTION_MAIN);  
  4.                 startMain.addCategory(Intent.CATEGORY_HOME);  
  5.                 startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  6.                 startActivity(startMain);  
  7.                 System.exit(0);  
  8.             } else {// android2.1
  9.                 ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);  
  10.                 am.restartPackage(getPackageName());  
  11.             }  

5. 小結

finish():結束當前Activity,不會立即釋放記憶體。遵循android記憶體管理機制。

exit():結束當前元件如Activity,並立即釋放當前Activity所佔資源。

killProcess():結束當前元件如Activity,並立即釋放當前Activity所佔資源。

restartPackage():結束整個App,包括service等其它Activity元件。

特別注意:除了finish()方法可以呼叫Activity的生命週期方法如onStop()、onDestroy(),其餘三種退出App均不會呼叫Activity的生命週期方法。除非,在呼叫這幾個方法之前或者之後主動呼叫Activity的生命週期方法。如:

  1. System.exit(int);  
  2. onDestroy();  

<!--EndFragment-->