Android之完美退出方法

來源:互聯網
上載者:User

標籤:

為什麼要寫這篇文章?

網上有很多種退出方法,可實際上很多方法都不通用(在某個版本下可用,到了另一個版本就不行),或者方法的實際效果根本就和其描述不符(也不知道那些發帖的人測沒測試過)。

但我們的需求又確實存在。在某些情況下,我們需要在應用中開啟多個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就能夠被關閉了。

 

 

 

最後 假設我們有下面一種需求,對上邊的內容進行總結:

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一定還是在棧中的老位置,而不是到棧頂端。

 

 

這個方法我挺喜歡。 

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

還有一種比較流行的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();

 

有人反映,如果程式崩潰,可能會導致該類的被迫關閉並重建,使得前期放入的Activity無法正常關閉。

 

—————————————分割線—————————————————

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

@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方法,這樣就不會有錯誤視窗彈出。程式完全退出。

 

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

Android之完美退出方法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.