如何安全退出已調用多個Activity的Application?


對於單一Activity的應用來說,退出很簡單,直接finish()即可。
當然,也可以用killProcess()和System.exit()這樣的方法。

但是,對於多Activity的應用來說,在打開多個Activity后,如果想在最后打開的Activity直接退出,上邊的方法都是沒有用的,因為上邊的方法都是結束一個Activity而已。
當然,網上也有人說可以。
就好像有人問,在應用里如何捕獲Home鍵,有人就會說用keyCode比較KEYCODE_HOME即可,而事實上如果不修改framework,根本不可能做到這一點一樣。
所以,最好還是自己親自試一下。

那么,有沒有辦法直接退出整個應用呢?
在2.1之前,可以使用ActivityManager的restartPackage方法。
它可以直接結束整個應用。在使用時需要權限android.permission.RESTART_PACKAGES。
注意不要被它的名字迷惑。

可是,在2.2,這個方法失效了。
在2.2添加了一個新的方法,killBackgroundProcesses(),需要權限 android.permission.KILL_BACKGROUND_PROCESSES。
可惜的是,它和2.2的restartPackage一樣,根本起不到應有的效果。

另外還有一個方法,就是系統自帶的應用程序管理里,強制結束程序的方法,forceStopPackage()。
它需要權限android.permission.FORCE_STOP_PACKAGES。
並且需要添加android:sharedUserId="android.uid.system"屬性
同樣可惜的是,該方法是非公開的,他只能運行在系統進程,第三方程序無法調用。
因為需要在Android.mk中添加LOCAL_CERTIFICATE := platform。
而Android.mk是用於在Android源碼下編譯程序用的。

從以上可以看出,在2.2,沒有辦法直接結束一個應用,而只能用自己的辦法間接辦到。

現提供幾個方法,供參考:

1、拋異常強制退出:
該方法通過拋異常,使程序Force Close。
驗證可以,但是,需要解決的問題是,如何使程序結束掉,而不彈出Force Close的窗口。

2、記錄打開的Activity:
每打開一個Activity,就記錄下來。在需要退出時,關閉每一個Activity即可。

3、發送特定廣播:
在需要結束應用時,發送一個特定的廣播,每個Activity收到廣播后,關閉即可。

4、遞歸退出
在打開新的Activity時使用startActivityForResult,然后自己加標志,在onActivityResult中處理,遞歸關閉。

除了第一個,都是想辦法把每一個Activity都結束掉,間接達到目的。
但是這樣做同樣不完美。
你會發現,如果自己的應用程序對每一個Activity都設置了nosensor,在兩個Activity結束的間隙,sensor可能有效了。
但至少,我們的目的達到了,而且沒有影響用戶使用。

為了編程方便,最好定義一個Activity基類,處理這些共通問題。

下面是轉載的一片文章
Android之完美退出方法(2.1-2.2-2.3SDK版本測試通過)[轉]
因為網上有很多種退出方法,可是實際上很多方法都不通用(在某個版本下可用,到了另一個版本就不行),或者方法的實際效果根本就和其描述不符(也不知道那些發帖的人測沒測試過)。

但我們的需求又確實存在。在某些情況下,我們需要在應用中打開多個Activity,但如果僅僅使用finish()方法就不能在需要的時候達到一次性退 出的效果,自己作為一個Android退出問題的受害者,通過良久思考和實際測試,找到了一個比較不錯的,在2.1-2.2-2.3版本下都通用的完全退 出方法(2.1版本也基本可以代表1.5~2.1版本)!


PS:測試全部在模擬器環境下進行

我首先進行一下說明,下面兩種方法效果完全相同
1,android.os.Process.killProcess(android.os.Process.myPid()) ; (這是Dalvik VM的本地方法)
2,System.exit(0); (常規java、c#的標准退出法,返回值為0代表正常退出 )
之后我的說明全部以android.os.Process.killProcess(android.os.Process.myPid()) 方法為准。

另外,我后邊所說的
程序入口即為在AndroidManifest.xml中配置為如下語句的Activity

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

下面開始我們的Android完美退出之旅:

先從網上的一段說明說起:
A->B
B中執行android.os.Process.killProcess(android.os.Process.myPid());
結果是結束了B,然后重新啟動A。
實際情況是這樣:A為程序入口,B中調用killProcess(android.os.Process.myPid())操作,實際上是將程序入口A和執行該語句的Activity B都關閉,並重新啟動新的程序入口A。
所以,如果過程是A->B->C
則實際情況是:A為程序入口,C中調用killProcess(android.os.Process.myPid())操作將程序入口A和執行該語句的Activity C都關閉,並重新啟動新的程序入口A(
在Activity窗口歷史棧當中,舊A 被關閉,新A 仍然會被放置在 舊A 所在的棧位置,不會到達棧頂端)。

PS: 如果killProcess(android.os.Process.myPid())或System.exit(0)是在程序入口A處執行,則是將入口A關閉,不會再開啟新的A.

有人要問了,B Activity呢?
B還存在着,B Activity沒有被關閉。
如何解決這個問題?
首先說明一點

android.os.Process.killProcess(android.os.Process.myPid()) ;語句執行之后,后邊的代碼都將不再執行;
而finish();或startActivity(A.this,B.class);語句在執行完成后仍舊會執行后續的代碼。(使用Thread.sleep多次驗證,不用擔心finish()過后不能startActivity了,相反也一樣)。
所以,我們就可以充分利用這一點,既然finish();和startActivity(A.this,B.class);語句在執行后仍然可以執行后續代碼操作,那我們可以將之組合在一個代碼片段中,即
startActivity(new Intent(B.this, C.class));
finish();

finish();
startActivity(new Intent(B.this, C.class));
都是可以的,我們在B中使用該代碼段,既將B Activity關閉了,也打開了C Activity,之前的問題Done!
如果你還有D,E,F ... 那也一樣,在每次跳轉到下一個Activity時,將finish()一塊用上。使用這種方式,多余的Activity就能夠被關閉了。


PS:很囧的是,在我自己的Android應用中,在程序入口處調用這種組合代碼,會直接將新開啟的Activity也一並關閉,但在我創建的簡單工程當中卻不會有這種情況,不知道為什么,還在尋找原因中......(也請高手指點一二)


最后 假設我們有下面一種需求,對上邊的內容進行總結:
Activity的開啟過程為 1.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index,在4.Index中實現退出,Index為程序入口。

Index
退出:就是最簡單的finish();
跳轉:也是最簡單的 startActivity(new Intent(Index.this, A_Activity.class));


A_Activity
退回到首界面:分兩種情況
1,需要Index更新(我的Index就有這個需求,首頁面色彩發生變化)
使用android.os.Process.killProcess(android.os.Process.myPid()) ;關閉自己和之前的Index,創建新的Index;
2,不需要新的Index,Index無變化
使用最簡單的finish(),並且效率還要高些。
跳轉:
finish();
startActivity(new Intent(A_Activity.this, C.class));
關閉自身,開啟除 Index 之外的其它Activity。


B_Activity
操作與 A_Activity 相同。當然跳轉語句變成了
finish();
startActivity(new Intent(B_Activity.this, Index.class));



**************************************************************************

上邊的內容就是這樣了,下面我再告訴你另外一種方法,可以實現不關閉中途的Activity,而是在后邊的操作中一次性關閉前邊開啟的所有Activity,可以滿足一些人通過按返回鍵返回上一個界面的要求!
通過Android的窗口類提供的歷史棧,巧妙利用stack的原理,我們在Intent中加入標志 Intent.FLAG_ACTIVITY_CLEAR_TOP。

Intent intent = new Intent();
intent.setClass(One.this, Two.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

假定有如下需求:
1.Index --> 2.A_Activity --> 3.B_Activity

在3中設置 intent.setClass(B_Activity.this, Index.class);
跳轉后,程序會從棧頂逐個向后查找,直到找 到棧中最近的Index,然后將這一路找到的Activity全部關閉,包括1、2、3(也就不需要像我先前的方法一路finish了,也保留了途中經過 的Activity),最后再自動建一個新的Index Activity放到棧頂的位置,接下來在Index窗口中使用finish方法即可退出。

如果沒有理解,看這個例子:
如果3中設置的是 intent.setClass(B_Activity.this, A_Activity.class);
則是將2,3關閉,再新建一個4.A_Activity,棧中就變成了
1.Index --> 4.A_Activity,懂了吧!
值得注意的是,在下面的情況中
1
.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index --> 5.A_Activity
在5中使用intent.setClass(A_Activity.this, B_Activity.class);
結果不是
1.Index --> 2.A_Activity --> 6.B_Activity
而是
1.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index--> 5.A_Activity --> 6.B_Activity,因為4(程序入口)的存在,所以5對棧的操作不會到達3,而是發現4、5中都沒有B_Activity后,沒有關閉任何 Activity,只在棧頂端新建了一個6.B_Activity。
這也間接說明了Dalvik虛擬機的遍歷算 法只進行到最近的程序入口,就認為后邊沒有該程序的Activity了。所以良好的Android編程習慣是,新建一個程序入口時,一定把老程序入口關 掉,這也解釋了為什么用killProcess方法更新后的程序入口Index一定還是在棧中的老位置,而不是到棧頂端。


以上都是我通過新建的一個簡單工程測試驗證過的。
不過很囧的是,這種退出方法,我在自己的應用程序下測試,結果都是直接退出到home界面,連Index界面都沒有出現,還想請高手賜教一下這里邊深層次的原因。


**************************************************************************
還有一種比較流行的Android經典完美退出方法,使用
單例模式創建一個Activity管理對象,該對象中有一個Activity容器(具體實現自己處理,使用LinkedList等)專門負責存儲新開啟的每一個Activity,並且容易理解、易於操作,非常不錯!
MyApplication類(儲存每一個Activity,並實現關閉所有Activity的操作)
public class MyApplication extends Application {

private List<Activity> activityList = new LinkedList<Activity>();
private static MyApplication instance;

private MyApplication()
{
}

//單例模式中獲取唯一的MyApplication實例
public static MyApplication getInstance()
{
if(null == instance)
{
instance = new MyApplication();
}
return instance;
}

//添加Activity到容器中
public void addActivity(Activity activity)
{
activityList.add(activity);
}

//遍歷所有Activity並finish
public void exit()
{
for(Activity activity:activityList)
{
activity.finish();
}
System.exit(0);
}
}

在每一個Activity中的onCreate方法里添加該Activity到MyApplication對象實例容器中
MyApplication.getInstance().addActivity(this);
在需要結束所有Activity的時候調用exit方法
MyApplication.getInstance().exit();

個人非常喜歡這種方法!(非常感謝
flyrabbits
的幫助)


—————————————分割線—————————————————
我對其他一些退出方法進行的一點介紹和點評(不到之處還請指正):

@
restartPackage(getPackageName())(具體就不介紹了)
我在SDK2.1版本下開發的一款小軟件,放到Android2.2或2.3操作系統上無法退出,因為restartPackage方法在SDK2.1以 后版本已經被廢棄不用了,理由是因為它不安全,可能關閉同其他應用程序共享的Service,而這個Service別人還要用呢,你給別人關了就不對了。


@
有人說的終極退出方法:
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
System.exit(0);
實際上這種方法只是返回了Home頁面,如果你再次進入應用,你會發現進入的首界面是你先前沒有關閉的Activity。


@
調用系統隱藏forceStopPackage方法,這里是通過映射調用(也有其他方法)
Method method = null;
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
try {
method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String.class);
method.invoke(manager,getPackageName());
} catch (Exception e) {
Log.d("force",e.getMessage());
}
我在SDK2.2和2.3的測試結果是,出現NULLPointerException,彈出錯誤窗口,程序被迫關閉,和預想的正常退出有差別。不過我們 可以通過修改基類實現自己的Thread.UncaughtExceptionHandler接口的uncaughtException方法,這樣就不會 有錯誤窗口彈出。程序完全退出。


@和上面一樣,不過這是故意制造異常退出(上邊是無意制造的異常),但我認為這畢竟是下策。

---------------------------------------------------------------------------------------------------

以上是網上的一些作法, 其實也可以通過intent的flag實現 , intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一個新的acitvity,然后在新的activ ity的onCreate()方法里面finish掉.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM