上家公司因為某些原因離職了,于是每天出去面試也沒時間來寫我的博客了,這兩天結束了十來天的動蕩,算是穩定下來了,又重新拾起紙筆,寫上兩筆。
看公司代碼的時候就在想,一個維護了兩年的項目勢必是有他獨特的地方的,于是乎,在很多小細節的地方,以前雖然也面試準備過,可是確實沒寫過。這些東西,可能看起來不起眼,平時沒什么用,可能在某些時候避免了程序崩潰的危險呢。
比如,今天就實踐一個退出很多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);
}