天天看點

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

轉自:http://263229365.iteye.com/blog/1283914

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

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

1. finish()方法

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

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

view plain print ?

  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)。

view plain print ?

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

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

view plain print ?

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

2. killProcess

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

view plain print ?

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

3. exit

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

view plain print ?

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

示例代碼,如下所示:

view plain print ?

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

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

view plain print ?

  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這個類的該方法源碼:

view plain print ?

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

其api說明:

view plain print ?

  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()方法。

view plain print ?

  1. static void exit(int status) {  
  2.     boolean runMoreFinalizers = false;  
  3.     synchronized (lock) {  
  4.         if (status != 0) runFinalizersOnExit = false;  
  5.         switch (state) {  
  6.         case RUNNING:     
  7.         state = HOOKS;  
  8.         break;  
  9.         case HOOKS:       
  10.         break;  
  11.         case FINALIZERS:  
  12.         if (status != 0) {  
  13.             halt(status);  
  14.         } else {  
  15.             runMoreFinalizers = runFinalizersOnExit;  
  16.         }  
  17.         break;  
  18.         }  
  19.     }  
  20.     if (runMoreFinalizers) {  
  21.         runAllFinalizers();  
  22.         halt(status);  
  23.     }  
  24.     synchronized (Shutdown.class) {  
  25.         sequence();  
  26.         halt(status);  
  27.     }  
  28.     }  

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

view plain print ?

  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方法

view plain print ?

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

首先需要建立ActivityManager對象,然後調用restartPackage()方法(如果有興趣的話,可以看源碼)。

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

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

view plain print ?

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

更加詳細的說明,如下:

view plain print ?

  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類的下面這個方法:

view plain print ?

  1. public void killBackgroundProcesses (String packageName)  

api 文檔說的很清楚:

view plain print ?

  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.  

另外,需要使用權限:

view plain print ?

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

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

view plain print ?

  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.             }  

關于android.os.Build.VERSION.SDK_INT,可以參考 http://blog.csdn.net/androidbluetooth/article/details/6778422

5. 小結

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

exit():結束目前元件如Activity,并立即釋放目前Activity所占資源。

killProcess():結束目前元件如Activity,并立即釋放目前Activity所占資源。

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

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

view plain print ?

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

<!--EndFragment-->