安全退出app,activoty栈管理

前言

由于一个同学问到我如何按照一个流程走好之后回到首页,我以前看到过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中,点击完成注册点击事件

 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);  
         }  
     }  
 }  

之后在注册流程中的对应步骤的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);  
     }  

使用流程和方法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();  
         }  
     }  
 };  

在流程中的每步Activity中,初始化广播,之后在点击完成注册时候,发送广播

 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);  
     }  

之后在流程的Activity中调用带请求码的Intent跳转意图。

 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();  
     }  
 });  

优缺点:

方案6(不推荐)

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

总结

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

源码DEMO

最后附上源码:下载地址

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏xingoo, 一个梦想做发明家的程序员

【java开发系列】—— java输入输出流

前言   任何语言输入输出流都是很重要的部分,比如从一个文件读入内容,进行分析,或者输出到另一个文件等等,都需要文件流的操作。这里简单介绍下reader,w...

27570
来自专栏xingoo, 一个梦想做发明家的程序员

JAVA利用poi获取world文件内容

  本文主要简单介绍了利用poi包,读取world文件内容。   这个依然存在版本的问题,只能读取doc结尾的老版本文件。   话不多说,上代码: 1 imp...

25460
来自专栏xingoo, 一个梦想做发明家的程序员

(JAVA版)冒泡排序

核心代码: public void bubbleSort(){ for(int i=0;i<length-1;i++){ ...

24150
来自专栏xingoo, 一个梦想做发明家的程序员

【java开发系列】—— 集合使用方法

前言   在java中提供给我们了一些类似C++泛型的简单集合,list,set,map等。这里,简单介绍一下这些集合容器的使用方法,以及复杂对象元素的自定...

25170
来自专栏xingoo, 一个梦想做发明家的程序员

【java开发系列】—— struts2简单入门示例

前言   最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的。   stru...

21450
来自专栏xingoo, 一个梦想做发明家的程序员

如何在Eclipse中添加Tomcat的jar包

右键项目工程,点击Java Build Path ? 点击Add Library,选择Server Runtime ? 选择Tomcat版本 image.png...

21070
来自专栏xingoo, 一个梦想做发明家的程序员

【java开发系列】—— JDK安装

前言   作为一个java开发者,安装JDK是不可避免的,但是配置路径却总是记不住,百度也有很多参考例子。这里仅仅当做以后参考的笔记记录。   说到JD...

212100
来自专栏xingoo, 一个梦想做发明家的程序员

Java堆外内存之突破JVM枷锁

对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收;而使用的内存是由JVM控制的。 那么,什么时机会进行垃圾...

39290
来自专栏xingoo, 一个梦想做发明家的程序员

JAVA利用jxl读取Excel内容

  JAVA可以利用jxl简单快速的读取文件的内容,但是由于版本限制,只能读取97-03  xls格式的Excel。 1 import java.io.Fil...

24150
来自专栏xingoo, 一个梦想做发明家的程序员

JAVA利用jxl读取Excel内容

  JAVA可以利用jxl简单快速的读取文件的内容,但是由于版本限制,只能读取97-03  xls格式的Excel。 1 import java.io.Fil...

22270

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励