从注册流程 分析如何安全退出多个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 这个问题。
其实,这个问题的解决方案有好多种,并且各有各的优缺点,下面就罗列出多个方案以及各个方案的优缺点所在,以便用户根据需求选择。
知识结构
首先,通过大致的思维导图罗列出了以下几个知识点,来帮助你去分析学习:
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中,点击完成注册点击事件
- 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);
- }
- });
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进行操作(推入,推出,弹出)
代码:
- 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);
- }
- }
- }
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有点类似。
代码:
- 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);
- }
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里面,便于维护。
代码:
- /**
- * 初始化退出广播
- */
- 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();
- }
- }
- };
/**
* 初始化退出广播
*/
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中,初始化广播,之后在点击完成注册时候,发送广播
- btn_finish.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- getApplicationContext().sendBroadcast(new Intent(INIENT_FINISH));
- }
- });
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里面便于查看。
代码:
- /** 关闭时候的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);
- }
/** 关闭时候的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跳转意图。
- startActivityForResult(new Intent(getApplicationContext(), SecondStep.class),FINISH_REQUESTCODE);
startActivityForResult(new Intent(getApplicationContext(), SecondStep.class),FINISH_REQUESTCODE);
在最后完成注册流程的时候通过以下方式返回:
- btn_finish.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- setResult(FINISH_RESULTCODE);
- finish();
- }
- });
btn_finish.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
setResult(FINISH_RESULTCODE);
finish();
}
});
优缺点:
方案6(不推荐)
方法:方法有人说可以使用抛出异常来退出,可是这样会影响到用户体验,所以不推荐
总结
以上便是我从注册流程分析如何安全退出多个ACTIVITY 的汇总总结,综上所述,博主觉得方案1是最可行的方法,如有什么错误之处,望大家提出,马上改正。
源码DEMO
最后附上源码:下载地址
(以上方式中都已经讲到了对应的方法和代码,源码可以更好的帮助你去体验下这几种方式的使用流程)
从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)的更多相关文章
- 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)
前言 由于一个同学问到我如何按照一个流程走好之后回到首页,我以前看到过4个解决方案,后来发现有做个记录和总结的必要,就写了这篇博文.(之前看小强也写过一篇,这里通过自身的分析完整的总结一下以下6种 ...
- 从注冊流程 分析怎样安全退出多个Activity 多种方式(附DEMO)
前言 因为一个同学问到我怎样依照一个流程走好之后回到首页.我曾经看到过4个解决方式,后来发现有做个记录和总结的必要,就写了这篇博文. (之前看小强也写过一篇,这里通过自身的分析完整的总结一下下面6种方 ...
- 基于GBT28181:SIP协议组件开发-----------第三篇SIP注册流程分析实现
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3941172.html,qq:1269122125. 上两章节简要的 ...
- S3C6410串口平台设备注册流程分析
1.mdesc->map_io() start_kernel -->setup_arch(&command_line); -->paging_init(mdesc); --& ...
- VLC架构及流程分析
0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析. 0x01 源码结构(Android Java相关的暂未分析) # bui ...
- Android8.1 开关VOLTE流程分析
前言 最近有需求需要实现插卡默认打开Volte功能,顺带研究了下Volte的流程,在此做个记录 开始 从Settings设置界面入手,网络和互联网-->移动网络-->VoLTE高清通话(电 ...
- boost.asio源码剖析(三) ---- 流程分析
* 常见流程分析之一(Tcp异步连接) 我们用一个简单的demo分析Tcp异步连接的流程: #include <iostream> #include <boost/asio.hpp& ...
- cinder侧挂载卷流程分析
cinder侧挂载卷流程分析,存储类型以lvm+iscsi的方式为分析基础cinder侧主要调用了三个接口1)reserve_volume: 把volume的状态改为attaching,阻止其它节点执 ...
- Openfire注册流程代码分析
Openfire注册流程代码分析 一.客户端/服务端注册用户流程 经过主机连接消息确认后,客户端共发送俩条XML完成注册过程.服务器返回两条XML. 注:IQ消息节点用于处理用户的注册.好友.分组.获 ...
随机推荐
- CSS padding margin border属性
主要定义四个区域:内容(content).内边距(padding).边框(border)和外边距(margin) margin:层的边框以外留的空白 background-color:背景颜色 bac ...
- window下查看TCP端口连接情况
window下查看TCP端口连接情况:netstat -ano -p tcp|findstr 10001
- Lua windows环境搭建
Lua语言的小巧和功能强大在朋友做的一个项目中得以验证,自己也尝试着了解一下,首先在window系统上搭建一个学习环境. 官网:https://www.lua.org/ 搭建运行环境提供2种方式,源码 ...
- 最短路径—Dijkstra算法
Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Di ...
- 学习node.js 第4篇 建立一个最小的web聊天系统
我们生活在一个实时的世界里,有什么比聊天更加实时吗?那就让我们先写一个基于TCP 的聊天服务器吧,并且支持Telnet 连接.这很容易,而且能够完全用Node来编写.首先,我们需要在Node 中包含T ...
- [Elixir009]像GenServer一样用behaviour来规范接口
1.Behaviour介绍 Erlang/Elixir的Behaviour类似于其它语言中的接口(interfaces),本质就是在指定behaviours的模块中强制要求导出一些指定的函数,否则编译 ...
- 卫星轨道和两行数据TLE
最近由于Sino-2和北斗的关系,很多网友贴了表示卫星运行轨道的TLE数据.这里想对卫星轨道参数和TLE的格式做一个简单介绍.虽然实际上没有人直接读TLE数据,而都是借助软件来获得卫星轨道和位置信息, ...
- Linux经常用到的命令
1. Linux下用vim打开配置文件乱码,在终端输入:“LANG=”即可. 2. 查看端口是否被占用: 3. netstat -anp | grep port netstat -ltn 4. lso ...
- java <? super Fruit>与<? extends Fruit>
package Test2016; import java.util.ArrayList; import java.util.List; public class Test2016 { public ...
- Xamarin 的 MVVM 之 Caliburn.Micro
约定 Caliburn.Micro 以下简称 CMXamarin.Form 以下简称 XF 摘要CM 当前已释出 3.0 beta 版https://github.com/Caliburn-Micro ...