• 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)


     
     
      
       

     
     
     

    前言

     

    由于一个同学问到我如何按照一个流程走好之后回到首页,我以前看到过4个解决方案,后来发现有做个记录和总结的必要,就写了这篇博文。(之前看小强也写过一篇,这里通过自身的分析完整的总结一下以下6种方案,并加上一个DEMO便于大家了解大体流程)

    在android的用户交互中,按钮触发的意图(Intent)跳转会为你重新打开新的一个界面活动(Activity),对于之前的界面根据需求进行摧毁(Finish())或则保留。

    如果一个交互流程中,是从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.Activity的启动模式                        

    2.intent:  Flags属性,以及其显、隐式        

    3.Application : 全局的使用

    4.Activity:  onActivityResult(int requestCode, int resultCode, Intent data)方法

    5.栈的概念:Last-In/First-Out(LIFO)   ---  后进先出的原则 

    6.BroadcastReceiver 广播

    7.栈的引申的知识点:(1)ArrayList和LinkedList的区别   (2)android 栈和队列

    以上的 (1)Activity的启动模式  (2)intent:  Flags属性  (3)栈的概念         

    我通过一篇文章写明了他们3者的联系可以点击以下链接查看

    Activity启动模式 及 Intent Flags 与 栈 的关联分析

    具体方案

     

    方案1

    方法:采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序(多activity)

    思路:通过Intent的Flags来控制堆栈去解决

    android中,每打开一个Activity,便会在栈中加入一个Activity,当该Activity被摧毁后,栈中便移除了它,并且栈中的Activity是按照开打的先后顺序依次排排列的。

    Android的窗口类提供了历史栈,我们可以通过stack的原理来巧妙的实现,这里我们在A窗口打开B窗口时在Intent中直接加入标 志 Intent.FLAG_ACTIVITY_CLEAR_TOP,这样开启B时将会清除该进程空间的所有Activity。

    代码:

    在注册流程最后的FourthStep.class中,点击完成注册点击事件

    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         Intent intent = new Intent(INTENT_METHOD_FIRST_SINGUP);  
    7.         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
    8.         startActivity(intent);  
    9.     }  
    10. });  
    		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。

    优缺点:

    优:使用对栈的巧妙利用,不会赞成内存无故占用等问题,个人认为这个方法是首选。

    方案2

    方法通过堆栈管理器去管理

    思路:通过堆栈管理器,对Stack进的存储Activity进行操作(推入,推出,弹出)

    代码:

    1. public class StackManager {  
    2.     /** 
    3.      * Stack 中对应的Activity列表  (也可以写做 Stack<Activity>) 
    4.      */  
    5.     private static Stack mActivityStack;  
    6.     private static StackManager mInstance;  
    7.   
    8.     /** 
    9.      * @描述 获取栈管理工具 
    10.      * @return ActivityManager 
    11.      */  
    12.     public static StackManager getStackManager() {  
    13.         if (mInstance == null) {  
    14.             mInstance = new StackManager();  
    15.         }  
    16.         return mInstance;  
    17.     }  
    18.   
    19.     /** 
    20.      * 推出栈顶Activity 
    21.      */  
    22.     public void popActivity(Activity activity) {  
    23.         if (activity != null) {  
    24.             activity.finish();  
    25.             mActivityStack.remove(activity);  
    26.             activity = null;  
    27.         }  
    28.     }  
    29.   
    30.     /** 
    31.      * 获得当前栈顶Activity 
    32.      */  
    33.     public Activity currentActivity() {  
    34.         //lastElement()获取最后个子元素,这里是栈顶的Activity  
    35.         if(mActivityStack == null || mActivityStack.size() ==0){  
    36.             return null;  
    37.         }  
    38.         Activity activity = (Activity) mActivityStack.lastElement();  
    39.         return activity;  
    40.     }  
    41.   
    42.     /** 
    43.      * 将当前Activity推入栈中 
    44.      */  
    45.     public void pushActivity(Activity activity) {  
    46.         if (mActivityStack == null) {  
    47.             mActivityStack = new Stack();  
    48.         }  
    49.         mActivityStack.add(activity);  
    50.     }  
    51.   
    52.     /** 
    53.      * 弹出指定的clsss所在栈顶部的中所有Activity 
    54.      * @clsss : 指定的类  
    55.      */  
    56.     public void popTopActivitys(Class clsss) {  
    57.         while (true) {  
    58.             Activity activity = currentActivity();  
    59.             if (activity == null) {  
    60.                 break;  
    61.             }  
    62.             if (activity.getClass().equals(clsss)) {  
    63.                 break;  
    64.             }  
    65.             popActivity(activity);  
    66.         }  
    67.     }  
    68.       
    69.     /** 
    70.      * 弹出栈中所有Activity 
    71.      */  
    72.     public void popAllActivitys() {  
    73.         while (true) {  
    74.             Activity activity = currentActivity();  
    75.             if (activity == null) {  
    76.                 break;  
    77.             }  
    78.             popActivity(activity);  
    79.         }  
    80.     }  
    81. }  
    public class StackManager {
    	/**
    	 * Stack 中对应的Activity列表  (也可以写做 Stack<Activity>)
    	 */
    	private static Stack mActivityStack;
    	private static StackManager mInstance;
    
    	/**
    	 * @描述 获取栈管理工具
    	 * @return ActivityManager
    	 */
    	public static StackManager getStackManager() {
    		if (mInstance == null) {
    			mInstance = new StackManager();
    		}
    		return mInstance;
    	}
    
    	/**
    	 * 推出栈顶Activity
    	 */
    	public void popActivity(Activity activity) {
    		if (activity != null) {
    			activity.finish();
    			mActivityStack.remove(activity);
    			activity = null;
    		}
    	}
    
    	/**
    	 * 获得当前栈顶Activity
    	 */
    	public Activity currentActivity() {
    		//lastElement()获取最后个子元素,这里是栈顶的Activity
    		if(mActivityStack == null || mActivityStack.size() ==0){
    			return null;
    		}
    		Activity activity = (Activity) mActivityStack.lastElement();
    		return activity;
    	}
    
    	/**
    	 * 将当前Activity推入栈中
    	 */
    	public void pushActivity(Activity activity) {
    		if (mActivityStack == null) {
    			mActivityStack = new Stack();
    		}
    		mActivityStack.add(activity);
    	}
    
    	/**
    	 * 弹出指定的clsss所在栈顶部的中所有Activity
    	 * @clsss : 指定的类 
    	 */
    	public void popTopActivitys(Class clsss) {
    		while (true) {
    			Activity activity = currentActivity();
    			if (activity == null) {
    				break;
    			}
    			if (activity.getClass().equals(clsss)) {
    				break;
    			}
    			popActivity(activity);
    		}
    	}
    	
    	/**
    	 * 弹出栈中所有Activity
    	 */
    	public void popAllActivitys() {
    		while (true) {
    			Activity activity = currentActivity();
    			if (activity == null) {
    				break;
    			}
    			popActivity(activity);
    		}
    	}
    }
    之后在注册流程中的对应步骤的Activity的onCreate()中把当前Activity推入栈列表,完成注册流程后,弹出栈列表中流程所涉及的Activity。 优缺点:

    缺:如果处理不当,容易造成不在当前界面的Activity被全局引用而摧毁不掉,内存得不到释放,从而无故占用不必要的内存。

    方案3:

    方法全局记录打开的Activity或通过一个自定义的类去管理打开的Activity

    思路:通过在Application中用一个列表来记录当前所打开的Activity,根据需求去遍历finish()。

    描述和方案2有点类似。

    代码:

     

    1. public class AppApplication extends Application {  
    2.     private static AppApplication mAppApplication;  
    3.     /** 当前打开的activity列表 */  
    4.     public ArrayList<Activity> activityList;  
    5.   
    6.     @Override  
    7.     public void onCreate() {  
    8.         // TODO Auto-generated method stub  
    9.         super.onCreate();  
    10.         mAppApplication = this;  
    11.     }  
    12.   
    13.     /** 获取Application */  
    14.     public static AppApplication getApp() {  
    15.         if (mAppApplication == null) {  
    16.             mAppApplication = new AppApplication();  
    17.         }  
    18.         return mAppApplication;  
    19.     }  
    20.   
    21.     /** 添加当前Activity 到列表中 */  
    22.     public void addActivity(Activity acitivity) {  
    23.         if(activityList == null){  
    24.             activityList = new ArrayList<Activity>();  
    25.         }  
    26.         activityList.add(acitivity);  
    27.     }  
    28.       
    29.     /** 清空列表,取消引用*/  
    30.     public void clearActivity(){  
    31.         activityList.clear();  
    32.     }  
    33.   
    34.     /** 遍历退出所有Activity */  
    35.     public void exit() {  
    36.         for (Activity activity : activityList) {  
    37.             activity.finish();  
    38.         }  
    39.         clearActivity();//千万记得清空取消引用。  
    40.         System.exit(0);  
    41.     }  
    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);
    	}
    使用流程和方法2类似。

    优缺点:

    缺:如果处理不当,容易造成不在当前界面的Activity被全局引用而摧毁不掉,内存得不到释放,从而无故占用不必要的内存。

    方案4

    方法使用广播机制解决

    思路:通过Activity创建的时候,设置监听广播,在注册流程最后步完成注册时候,发送广播进行遍历finish().

    描述:这里我把这些广播的初始化都写在了基类BaseActivity里面,便于维护。

    代码:

    1. /** 
    2.  * 初始化退出广播 
    3.  */  
    4. public void initFinishReceiver() {  
    5.     IntentFilter filter = new IntentFilter();  
    6.     filter.addAction(INIENT_FINISH);  
    7.     registerReceiver(mFinishReceiver, filter);  
    8. }  
    9.   
    10. /** 
    11.  * 监听是否退出的广播 
    12.  */  
    13. public BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {  
    14.   
    15.     @Override  
    16.     public void onReceive(Context context, Intent intent) {  
    17.         if (INIENT_FINISH.equals(intent.getAction())) {  
    18.             finish();  
    19.         }  
    20.     }  
    21. };  
    	/**
    	 * 初始化退出广播
    	 */
    	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中,初始化广播,之后在点击完成注册时候,发送广播
    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         getApplicationContext().sendBroadcast(new Intent(INIENT_FINISH));  
    7.     }  
    8. });  
    		btn_finish.setOnClickListener(new OnClickListener() {
    
    			@Override
    			public void onClick(View v) {
    				// TODO Auto-generated method stub
    				getApplicationContext().sendBroadcast(new Intent(INIENT_FINISH));
    			}
    		});
    优缺点:

    缺:开启过多的广播监听,觉得会浪费资源。

    方案5:

    方法通过Activity跳转中传递requestCode的之后根据onActivityResult(int requestCode, int resultCode, Intent data)中返回的resultCode遍历关闭Activity

    思路:使用startActivityForResult(intent, requestCode)方法跳转,并且通过

    描述:这里我把这些广播的初始化都写在了基类BaseActivity里面便于查看。

    代码:

     

    1. /** 关闭时候的requestCode请求码 */  
    2.     public final static int FINISH_REQUESTCODE = 1;  
    3.     /** 关闭时候的resultCode请求码 */  
    4.     public final static int FINISH_RESULTCODE = 1;  
    5.     /** 
    6.      * 方法5通过回调关闭的时候用到 
    7.      */  
    8.     @Override  
    9.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    10.         // TODO Auto-generated method stub  
    11.         if(requestCode == FINISH_REQUESTCODE ){  
    12.             if(resultCode == FINISH_RESULTCODE){  
    13.                 setResult(FINISH_RESULTCODE);  
    14.                 finish();  
    15.             }  
    16.         }  
    17.         super.onActivityResult(requestCode, resultCode, data);  
    18.     }  
    /** 关闭时候的requestCode请求码 */
    	public final static int FINISH_REQUESTCODE = 1;
    	/** 关闭时候的resultCode请求码 */
    	public final static int FINISH_RESULTCODE = 1;
    	/**
    	 * 方法5通过回调关闭的时候用到
    	 */
    	@Override
    	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    		// TODO Auto-generated method stub
    		if(requestCode == FINISH_REQUESTCODE ){
    			if(resultCode == FINISH_RESULTCODE){
    				setResult(FINISH_RESULTCODE);
    				finish();
    			}
    		}
    		super.onActivityResult(requestCode, resultCode, data);
    	}
    之后在流程的Activity中调用带请求码的Intent跳转意图。

     

    1. startActivityForResult(new Intent(getApplicationContext(), SecondStep.class),FINISH_REQUESTCODE);  
    startActivityForResult(new Intent(getApplicationContext(), SecondStep.class),FINISH_REQUESTCODE);
    在最后完成注册流程的时候通过以下方式返回:
    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         setResult(FINISH_RESULTCODE);  
    7.         finish();  
    8.     }  
    9. });  
    	btn_finish.setOnClickListener(new OnClickListener() {
    
    		@Override
    		public void onClick(View v) {
    			// TODO Auto-generated method stub
    			setResult(FINISH_RESULTCODE);
    			finish();
    		}
    	});

    优缺点:

    方案6(不推荐)

    方法方法有人说可以使用抛出异常来退出,可是这样会影响到用户体验,所以不推荐

    总结

    以上便是我从注册流程分析如何安全退出多个ACTIVITY 的汇总总结,综上所述,博主觉得方案1是最可行的方法,如有什么错误之处,望大家提出,马上改正。

    源码DEMO

    最后附上源码:下载地址   

    (以上方式中都已经讲到了对应的方法和代码,源码可以更好的帮助你去体验下这几种方式的使用流程)

  • 相关阅读:
    之前的博客
    用struts2 s2-045漏洞拿站记录
    修改BlackLowKey皮肤样式,增加占屏比
    SpringBoot自动配置原理
    CAS无锁技术
    CAS单点登录原理解析
    死锁与活锁的区别,死锁与饥饿的区别
    jvm问题
    jdk动态代理的实现原理
    抽象工厂
  • 原文地址:https://www.cnblogs.com/bigben0123/p/4481702.html
Copyright © 2020-2023  润新知