退出 android 應用程序

轉自:http://blog.csdn.net/veryitman/article/details/6574940

1. finish()方法


該方法可以結束當前 Activity,但是如果你的 App 有很多 Activity 的話,使用該方法顯得有點捉襟見肘了。
另外,還有一個方法 finishActivity (int requestCode) ,關於這個方法,先看看 sdk 的 api 說明吧!

public void finishActivity (int requestCode)
Since: API Level 1

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

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

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

還有,下面兩個方法,可以參閱文檔以及源碼研究一下。
finishActivityFromChild(Activity child, int requestCode)
finishFromChild(Activity child)

2. killProcess

通過調用 android.os.Process 的相關方法,結束 App,示例如下:
btn_exit.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
        android.os.Process.killProcess(android.os.Process.myPid());
    }
});

3. exit

我們知道,Java 的 exit(int code) 方法可以退出程序,通過查看該方法源碼,知道它實際上是調用下面的方法:
Runtime.getRuntime().exit(code);

示例代碼,如下所示:
btn_exit.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
    System.exit(0);//正常退出App
    }
});
接下來,我們研究一下這個方法。java.lang.System 這個類的該方法 jdk 說明:
exit
public static void exit(int status)

終止當前正在運行的 Java 虛擬機。參數用作狀態碼;根據慣例,非 0 的狀態碼錶示異常終止。
該方法調用 Runtime 類中的 exit 方法。該方法永遠不會正常返回。

調用 System.exit(n) 實際上等效於調用:

Runtime.getRuntime().exit(n)
 
參數:
status - 退出狀態。
拋出:
SecurityException - 如果安全管理器存在並且其 checkExit 方法不允許以指定狀態退出。
另請參見:
Runtime.exit(int)
也就是說,參數爲非0值的話是異常退出程序。參數爲0的話,就是正常退出。
看RunTime這個類的該方法源碼:


public void exit(int status) {
        SecurityManager security = System.getSecurityManager();
if (security != null) {
   security.checkExit(status);
}
Shutdown.exit(status);
}


其api說明:
exit
public void exit(int status)


通過啓動虛擬機的關閉序列,終止當前正在運行的 Java 虛擬機。此方法從不正常返回。可以將變量作爲一個狀態碼;根據慣例,非零的狀態碼錶示非正常終止。
虛擬機的關閉序列包含兩個階段。在第一個階段中,會以某種未指定的順序啓動所有已註冊的關閉鉤子 (hook)(如果有的話),並且允許它們同時運行直至結束。
在第二個階段中,如果已啓用退出終結,則運行所有未調用的終結方法。一旦完成這個階段,虛擬機就會暫停。
如果在虛擬機已開始其關閉序列後才調用此方法,那麼若正在運行關閉鉤子,則將無限期地阻斷此方法。
如果已經運行完關閉鉤子,並且已啓用退出終結 (on-exit finalization),那麼此方法將利用給定的狀態碼(如果狀態碼是非零值)暫停虛擬機;
否則將無限期地阻斷虛擬機。

System.exit 方法是調用此方法的一種傳統而便捷的方式。

參數:
status - 終止狀態。按照慣例,非零的狀態碼錶明非正常終止。
拋出:
SecurityException - 如果安全管理器存在,並且其 checkExit 方法不允許存在指定的狀態

另請參見:
SecurityException, SecurityManager.checkExit(int), addShutdownHook(java.lang.Thread), 
removeShutdownHook(java.lang.Thread), runFinalizersOnExit(boolean), halt(int)

該方法又是調用 Shutdown 這個類的 exit() 方法。


static void exit(int status) {
boolean runMoreFinalizers = false;
synchronized (lock) {
   if (status != 0) runFinalizersOnExit = false;
   switch (state) {
   case RUNNING:/* Initiate shutdown */
       state = HOOKS;
       break;
   case HOOKS:/* Stall and halt */
       break;
   case FINALIZERS:
       if (status != 0) {
           /* Halt immediately on nonzero status */
           halt(status);
       } else {
           /* Compatibility with old behavior:
            * Run more finalizers and then halt
           */
           runMoreFinalizers = runFinalizersOnExit;
      }
     break;
   }
}
if (runMoreFinalizers) {
   runAllFinalizers();
   halt(status);
}
synchronized (Shutdown.class) {
   /* Synchronize on the class object, causing any other thread
             * that attempts to initiate shutdown to stall indefinitely
    */
   sequence();
   halt(status);
}
    }

其中,runAllFinalizers() 是一個本地方法:
JNIEXPORT void JNICALL
Java_java_lang_Shutdown_runAllFinalizers(JNIEnv *env, jclass ignored)
{
    jclass cl;
    jmethodID mid;

    if ((cl = (*env)->FindClass(env, "java/lang/ref/Finalizer"))
         && (mid = (*env)->GetStaticMethodID(env, cl,
             "runAllFinalizers", "()V"))) {
                 (*env)->CallStaticVoidMethod(env, cl, mid);
     }
}
System.exit() 的參數是把退出原因返回給系統, 一般來說可以是任何的整數 。

0 表示正常退出,1 表示非正常 。
最後說一下 finish() 與 exit 方法的區別:
finish()是Activity的類方法,僅僅針對Activity,當調用 finish() 時,只是將活動推向後臺,並沒有立即釋放內存,活動的資源並沒有被清理;當調用 System.exit(0) 時,退出當前 Activity 並釋放資源(內存),但是該方法不可以結束整個 App 如有多個 Activty 或者有其他組件 service 等不會結束。
其實android的機制決定了用戶無法完全退出應用,當你的 application 最長時間沒有被用過的時候,android 自身會決定將 application 關閉了。

4. restartPackage 方法

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

首先需要創建 ActivityManager 對象,然後調用 restartPackage() 方法(如果有興趣的話,可以看源碼)。
注意:getPackageName 獲得當前應用包名稱,如 mark.zhang
使用這種方式來退出 App,需要權限:


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


加詳細的說明,如下:
void android.app.ActivityManager.restartPackage(String packageName)

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.

You must hold the permission android.Manifest.permission.RESTART_PACKAGES to be able to call this method.

Parameters:
    packageName The name of the package to be stopped.

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

注意一個問題:在 android2.2 之後,該方法不可以將應用程序結束,需要使用 ActivityManager 類的下面這個方法:
public void killBackgroundProcesses (String packageName)
api 文檔說的很清楚:
public void restartPackage (String packageName)

Since: API Level 3
This method is deprecated.
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.


另外,需要使用權限:


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


但是不管你怎麼樣折騰,還是無法退出App,嗚呼哀哉!這裏給出一個方法:
int currentVersion = android.os.Build.VERSION.SDK_INT;
if (currentVersion > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
    Intent startMain = new Intent(Intent.ACTION_MAIN);
    startMain.addCategory(Intent.CATEGORY_HOME);
    startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(startMain);
    System.exit(0);
} else {// android2.1
    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    am.restartPackage(getPackageName());
}

關於 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 的生命週期方法。如:
System.exit(int);
onDestroy();

項目經歷:
Actiivty A、B,A 啓動 B,在 B 中點擊 Back,然後回到 A,悲慘的一幕發生了,回到 A,A 居然也消失了......,並且沒有調用 A 的 onDestroy 方法。
百思不得其解,冷靜一下,既然點擊 Back,那麼肯定會調用 B 的 onDestroy 方法,於是,看到杯具的來源:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        android.os.Process.killProcess(android.os.Process.myPid());
    }


罪魁禍首是 android.os.Process.killProcess(android.os.Process.myPid()) 這句代碼,它將 app 這個 process kill 掉,所以,看不到 A 再現,並且不會調用 A 的 onDestroy 方法。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章