Android-完全退出當前應用程式-新公司學習到的細節

影響身邊的人發表於2016-08-18

上家公司因為某些原因離職了,於是每天出去面試也沒時間來寫我的部落格了,這兩天結束了十來天的動盪,算是穩定下來了,又重新拾起紙筆,寫上兩筆。

看公司程式碼的時候就在想,一個維護了兩年的專案勢必是有他獨特的地方的,於是乎,在很多小細節的地方,以前雖然也面試準備過,可是確實沒寫過。這些東西,可能看起來不起眼,平時沒什麼用,可能在某些時候避免了程式崩潰的危險呢。

比如,今天就實踐一個退出很多Activity的功能,在某些情況下保證安全。


借鑑一篇部落格中的例子吧:(註冊流程)

如果一個互動流程中,是從A開始,按照A - B - C - D - A這樣的順序進行的話,那麼B,C,D這3個活動介面會根據你D中最後的操作來進行保留或是摧毀,例如

(1)註冊流程中,在A介面點選註冊,通過B,C,D介面完成註冊後,B,C,D就隨之摧毀,而如果D中註冊不成功沒跳轉會A的話,那麼B,C,D就不能摧毀,之前所填的內容也必須保留。

(2)客戶端互動中,返回首頁按鈕,由於在頻繁的點選開啟過多的介面(如微信檢視朋友圈),返回首頁就必須一個一個back回去,所有有的客戶端為了優化使用者體驗,便會加入一個按鈕返回首頁(之前開啟的全部關閉)。


以上例子都涉及到了 --- 如何安全退出多個ACTIVITY 這個問題。

其實,這個問題的解決方案有好多種,並且各有各的優缺點,下面就羅列出多個方案以及各個方案的優缺點所在,以及本人所在專案採用的方案。


1. Dalvik VM的本地方法

android.os.Process.killProcess(android.os.Process.myPid()) //獲取PID System.exit(0); //常規java、c#的標準退出法,返回值為0代表正常退出

2. 工作管理員方法

首先要說明該方法執行在Android 1.5 API Level為3以上才可以,同時需要許可權

  ActivityManager am = (ActivityManager)getSystemService (Context.ACTIVITY_SERVICE);
  am.restartPackage(getPackageName());複製程式碼

系統會將,該包下的 ,所有程式,服務,全部殺掉,就可以殺乾淨了,要注意加上

 <uses-permission android:name=\"android.permission.RESTART_PACKAGES\"></uses-permission>複製程式碼

3. 我們知道Android的視窗類提供了歷史棧,我們可以通過stack的原理來巧妙的實現,這裡我們在A視窗開啟B視窗時在Intent中直接加入標 志 Intent.FLAG_ACTIVITY_CLEAR_TOP,這樣開啟B時將會清除該程式空間的所有Activity。

思路:通過Intent的Flags來控制堆疊去解決 android中,每開啟一個Activity,便會在棧中加入一個Activity,當該Activity被摧毀後,棧中便移除了它,並且棧中的Activity是按照開打的先後順序依次排排列的。 Android的視窗類提供了歷史棧,我們可以通過stack的原理來巧妙的實現,這裡我們在A視窗開啟B視窗時在Intent中直接加入標 志 Intent.FLAG_ACTIVITY_CLEAR_TOP,這樣開啟B時將會清除該程式空間的所有Activity。

btn_finish.setOnClickListener(new OnClickListener() {  

    @Override  
    public void onClick(View v) {  
        // TODO Auto-generated method stub  
        Intent intent = new Intent(INTENT_METHOD_FIRST_SINGUP);  
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
        startActivity(intent);  
    }  
});複製程式碼

其中的 INTENT_METHOD_FIRST_SINGUP 是登入介面的Intent隱式Action。

優缺點: 優:使用對棧的巧妙利用,不會造成記憶體無故佔用等問題


4.自定義一個Actiivty 棧,道理同上,不過利用一個單例模式的Activity棧來管理所有Activity。並提供退出所有Activity的方法。程式碼如下:

 public class ScreenManager {
 private static Stack<Activity> activityStack;
 private static ScreenManager instance;
 private  ScreenManager(){
 }
 public static ScreenManager getScreenManager(){
  if(instance==null){
   instance=new ScreenManager();
  }
  return instance;
 }
//退出棧頂Activity
 public void popActivity(Activity activity){
  if(activity!=null){
   activity.finish();
   activityStack.remove(activity);
   activity=null;
  }
 }

//獲得當前棧頂Activity
 public Activity currentActivity(){
  Activity activity=activityStack.lastElement();
  return activity;
 }

//將當前Activity推入棧中
 public void pushActivity(Activity activity){
  if(activityStack==null){
   activityStack=new Stack<Activity>();
  }
  activityStack.add(activity);
 }
 //退出棧中所有Activity
 public void popAllActivityExceptOne(Class cls){
  while(true){
   Activity activity=currentActivity();
   if(activity==null){
    break;
   }
   if(activity.getClass().equals(cls) ){
    break;
   }
   popActivity(activity);
  }
 }
}複製程式碼

5.全域性記錄開啟的Activity或通過一個自定義的類去管理開啟的Activity

思路:通過在Application中用一個列表來記錄當前所開啟的Activity,根據需求去遍歷finish()。

public class AppApplication extends Application {  
    private static AppApplication mAppApplication;  
    /** 當前開啟的activity列表 */  
    public ArrayList<Activity> activityList;  

    @Override  
    public void onCreate() {  
        // TODO Auto-generated method stub  
        super.onCreate();  
        mAppApplication = this;  
    }  

    /** 獲取Application */  
    public static AppApplication getApp() {  
        if (mAppApplication == null) {  
            mAppApplication = new AppApplication();  
        }  
        return mAppApplication;  
    }  

    /** 新增當前Activity 到列表中 */  
    public void addActivity(Activity acitivity) {  
        if(activityList == null){  
            activityList = new ArrayList<Activity>();  
        }  
        activityList.add(acitivity);  
    }  

    /** 清空列表,取消引用*/  
    public void clearActivity(){  
        activityList.clear();  
    }  

    /** 遍歷退出所有Activity */  
    public void exit() {  
        for (Activity activity : activityList) {  
            activity.finish();  
        }  
        clearActivity();//千萬記得清空取消引用。  
        System.exit(0);  
    }複製程式碼

優缺點: 缺:如果處理不當,容易造成不在當前介面的Activity被全域性引用而摧毀不掉,記憶體得不到釋放,從而無故佔用不必要的記憶體。


6.使用廣播機制解決

通過Activity建立的時候,設定監聽廣播,在註冊流程最後步完成註冊時候,傳送廣播進行遍歷finish().

程式碼:

/** 
 * 初始化退出廣播 
 */  
public void initFinishReceiver() {  
    IntentFilter filter = new IntentFilter();  
    filter.addAction(INIENT_FINISH);  
    registerReceiver(mFinishReceiver, filter);  
}  

/** 
 * 監聽是否退出的廣播 
 */  
public BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {  

    @Override  
    public void onReceive(Context context, Intent intent) {  
        if (INIENT_FINISH.equals(intent.getAction())) {  
            finish();  
        }  
    }  
};  
在流程中的每步Activity中,初始化廣播,之後在點選完成註冊時候,傳送廣播
[java] view plain copy 在CODE上檢視程式碼片派生到我的程式碼片
btn_finish.setOnClickListener(new OnClickListener() {  

    @Override  
    public void onClick(View v) {  
        // TODO Auto-generated method stub  
        getApplicationContext().sendBroadcast(new Intent(INIENT_FINISH));  
    }  
});  

缺:開啟過多的廣播監聽,覺得會浪費資源。複製程式碼

我這邊採用第三種方法,如下

public void exit() {
        Intent intent = new Intent();
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.setClass(this, HomePageActivity.class);
        intent.putExtra("exitApp", true);

        Misc.startActivity(intent);
    }複製程式碼

相關文章