为什么要写这篇文章?

网上有很多种退出方法,可实际上很多方法都不通用(在某个版本下可用,到了另一个版本就不行),或者方法的实际效果根本就和其描述不符(也不知道那些发帖的人测没测试过)。

但我们的需求又确实存在。在某些情况下,我们需要在应用中打开多个Activity,但如果仅仅使用finish()方法就不能在需要的时候达到一次性退出的效果,自己作为一个Android退出问题的受害者,通过良久思考和实际测试,找到了一个比较不错的,在2.1-2.2-2.3版本下都通用的完全退出方法(2.1版本也基本可以代表1.5~2.1版本)!

PS:测试全部在模拟器环境下进行

我首先进行一下说明,下面两种方法效果完全相同

1,android.os.Process.killProcess(android.os.Process.myPid()) ; (这是Dalvik VM的本地方法)

2,System.exit(0);   (常规java、c#的标准退出法,返回值为0代表正常退出 )

之后我的说明全部以android.os.Process.killProcess(android.os.Process.myPid()) 方法为准。

另外,我后边所说的程序入口即为在AndroidManifest.xml中配置为如下语句的Activity

<action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />

下面开始我们的Android完美退出之旅:

先从网上的一段说明说起:

A->B
B中执行android.os.Process.killProcess(android.os.Process.myPid());
  结果是结束了B,然后重新启动A。
实际情况是这样:A为程序入口,B中调用killProcess(android.os.Process.myPid())操作,实际上是将程序入口A和执行该语句的Activity B都关闭,并重新启动新的程序入口A。

所以,如果过程是A->B->C

则实际情况是:A为程序入口,C中调用killProcess(android.os.Process.myPid())操作将程序入口A和执行该语句的Activity C都关闭,并重新启动新的程序入口A(在Activity窗口历史栈当中,旧A 被关闭,新A 仍然会被放置在 旧A 所在的栈位置,不会到达栈顶端)。

PS: 如果killProcess(android.os.Process.myPid())或System.exit(0)是在程序入口A处执行,则是将入口A关闭,不会再开启新的A.

有人要问了,B Activity呢? B还存在着,B Activity没有被关闭。

如何解决这个问题?

首先说明一点

android.os.Process.killProcess(android.os.Process.myPid()) ;语句执行之后,后边的代码都将不再执行;

而finish();或startActivity(A.this,B.class);语句在执行完成后仍旧会执行后续的代码。(使用Thread.sleep多次验证,不用担心finish()过后不能startActivity了,相反也一样)。

所以,我们就可以充分利用这一点,既然finish();和startActivity(A.this,B.class);语句在执行后仍然可以执行后续代码操作,那我们可以将之组合在一个代码片段中,即

startActivity(new Intent(B.this, C.class));
      finish();

finish();

startActivity(new Intent(B.this, C.class));

都是可以的,我们在B中使用该代码段,既将B Activity关闭了,也打开了C Activity,之前的问题Done!

如果你还有D,E,F ... 那也一样,在每次跳转到下一个Activity时,将finish()一块用上。使用这种方式,多余的Activity就能够被关闭了。

 

最后 假设我们有下面一种需求,对上边的内容进行总结:

Activity的开启过程为 1.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index,在4.Index中实现退出,Index为程序入口。

Index
 退出:就是最简单的finish();
 跳转:也是最简单的 startActivity(new Intent(Index.this, A_Activity.class));
A_Activity
退回到首界面:分两种情况
1,需要Index更新(我的Index就有这个需求,首页面色彩发生变化)
使用android.os.Process.killProcess(android.os.Process.myPid()) ;关闭自己和之前的Index,创建新的Index;
2,不需要新的Index,Index无变化
使用最简单的finish(),并且效率还要高些。
跳转:
      finish();

startActivity(new Intent(A_Activity.this, C.class));

关闭自身,开启除 Index 之外的其它Activity。

B_Activity

操作与 A_Activity 相同。当然跳转语句变成了

finish();

startActivity(new Intent(B_Activity.this, Index.class));

*************************************************************************************************************

上边的内容就是这样了,下面我再告诉你另外一种方法,可以实现不关闭中途的Activity,而是在后边的操作中一次性关闭前边开启的所有Activity,可以满足一些人通过按返回键返回上一个界面的要求!

通过Android的窗口类提供的历史栈,巧妙利用stack的原理,我们在Intent中加入标志 Intent.FLAG_ACTIVITY_CLEAR_TOP。

Intent intent = new Intent();

intent.setClass(One.this, Two.class);  
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
    startActivity(intent);

假定有如下需求:

1.Index --> 2.A_Activity --> 3.B_Activity

在3中设置 intent.setClass(B_Activity.this, Index.class);

跳转后,程序会从栈顶逐个向后查找,直到找到栈中最近的Index,然后将这一路找到的Activity全部关闭,包括1、2、3(也就不需要像我先前的方法一路finish了,也保留了途中经过的Activity),最后再自动建一个新的Index Activity放到栈顶的位置,接下来在Index窗口中使用finish方法即可退出。

如果没有理解,看这个例子:

如果3中设置的是 intent.setClass(B_Activity.this, A_Activity.class);

则是将2,3关闭,再新建一个4.A_Activity,栈中就变成了

1.Index --> 4.A_Activity,懂了吧!

值得注意的是,在下面的情况中

1.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index --> 5.A_Activity

在5中使用intent.setClass(A_Activity.this, B_Activity.class);

结果不是

1.Index --> 2.A_Activity --> 6.B_Activity

而是

1.Index --> 2.A_Activity --> 3.B_Activity --> 4.Index --> 5.A_Activity --> 6.B_Activity,因为4(程序入口)的存在,所以5对栈的操作不会到达3,而是发现4、5中都没有B_Activity后,没有关闭任何Activity,只在栈顶端新建了一个6.B_Activity。

这也间接说明了Dalvik虚拟机的遍历算法只进行到最近的程序入口,就认为后边没有该程序的Activity了。所以良好的Android编程习惯是,新建一个程序入口时,一定把老程序入口关掉,这也解释了为什么用killProcess方法更新后的程序入口Index一定还是在栈中的老位置,而不是到栈顶端。

这个方法我挺喜欢。

**********************************************************************************************

还有一种比较流行的Android经典完美退出方法,使用单例模式创建一个Activity管理对象,该对象中有一个Activity容器(具体实现自己处理,使用LinkedList等)专门负责存储新开启的每一个Activity,并且容易理解、易于操作,非常不错!

MyApplication类(储存每一个Activity,并实现关闭所有Activity的操作)

public class MyApplication extends Application {

private List<Activity> activityList = new LinkedList<Activity>(); 
 private static MyApplication instance;
 
            private MyApplication()
            {
            }
             //单例模式中获取唯一的MyApplication实例 
             public static MyApplication getInstance()
             {
                            if(null == instance)
                          {
                             instance = new MyApplication();
                          }
                 return instance;

}
             //添加Activity到容器中
             public void addActivity(Activity activity)
             {
                            activityList.add(activity);
             }
             //遍历所有Activity并finish

public void exit()
             {

for(Activity activity:activityList)
                         {
                           activity.finish();
                         }

System.exit(0);

}
}

在每一个Activity中的onCreate方法里添加该Activity到MyApplication对象实例容器中

MyApplication.getInstance().addActivity(this);

在需要结束所有Activity的时候调用exit方法

MyApplication.getInstance().exit();

有人反映,如果程序崩溃,可能会导致该类的被迫关闭并重建,使得前期放入的Activity无法正常关闭。

—————————————分割线—————————————————

我对其他一些退出方法进行的一点介绍和点评(不到之处还请指正):

@restartPackage(getPackageName())(具体就不介绍了)

我在SDK2.1版本下开发的一款小软件,放到Android2.2或2.3操作系统上无法退出,因为restartPackage方法在SDK2.1以后版本已经被废弃不用了,理由是因为它不安全,可能关闭同其他应用程序共享的Service,而这个Service别人还要用呢,你给别人关了就不对了。

@有人说的终极退出方法:

Intent startMain = new Intent(Intent.ACTION_MAIN);
           startMain.addCategory(Intent.CATEGORY_HOME);
           startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
           startActivity(startMain);

System.exit(0);

实际上这种方法只是返回了Home页面,如果你再次进入应用,你会发现进入的首界面是你先前没有关闭的Activity。

@调用系统隐藏forceStopPackage方法,这里是通过映射调用(也有其他方法)

Method method = null;

ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        try {
         method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String.class);
         method.invoke(manager,getPackageName());
        } catch (Exception e) {
         Log.d("force",e.getMessage());

}

我在SDK2.2和2.3的测试结果是,出现NULLPointerException,弹出错误窗口,程序被迫关闭,和预想的正常退出有差别。不过我们可以通过修改基类实现自己的Thread.UncaughtExceptionHandler接口的uncaughtException方法,这样就不会有错误窗口弹出。程序完全退出。

@和上面一样,不过这是故意制造异常退出(上边是无意制造的异常),但我认为这毕竟是下策。

Android之完美退出方法的更多相关文章

  1. Android经典完美退出方法

    Android经典完美退出方法,使用单例模式创建一个Activity管理对象,该对象中有一个Activity容器(具体实现自己处理,使用LinkedList等)专门负责存储新开启的每一个Activit ...

  2. Android程序中有多个Activity时的可全部退出方法

    下面是代码.该方法的原理是用一个list记录每次启动的acitivity,在退出时候循环退出改list中保存的acitivity,这样就做到了所有的acitivity完美退出.   先写一个类继承Ap ...

  3. Android 一次退出所有activity的方法

    转自:http://bbs.51cto.com/thread-970933-1.html 因为android的API中没有提供一次性退出的接口所以想要在多activity的程序里面一次性退出的话就要费 ...

  4. 你真的有必要退出吗——再说Android程序的退出功能

    转自你真的有必要退出吗--再说Android程序的退出功能 搞Android开发有一段时间了,相信很多从Windows开发过来的Android程序员都习惯性地会跟我一样遇到过同一个问题:如何彻底退出程 ...

  5. Android中完全退出当前应用系统

    一.将统一管理Activity的类ActivityManager复制到工程里面. package com.jsmtr.www.Helper; import java.util.LinkedList; ...

  6. win10想说爱你不容易——安装.net3.5也是一个坑(已有完美解决方法)

    最终完美解决方法:经过多次波折,终于找到无法正常安装.net3.5的原因了,是因为已删除的用户还有注册表残留导致的,而且这个问题还会影响一个win10更新的安装,导致每天更新失败,撤销更新... 详见 ...

  7. Android 打造完美的侧滑菜单/侧滑View控件

    概述 Android 打造完美的侧滑菜单/侧滑View控件,完全自定义实现,支持左右两个方向弹出,代码高度简洁流畅,兼容性高,控件实用方便. 详细 代码下载:http://www.demodashi. ...

  8. Android程序完全退出

    有几个activity,有一需求是在一个activityA点击back键退出系统而不是跳到之前的activity       首先想到的是清空activityA的堆栈,使用intent.setFlag ...

  9. Android 自定义AlertDialog退出对话框

    Android 自定义AlertDialog退出对话框 转 https://blog.csdn.net/wkh11/article/details/53081634在项目中很多时候会出现点击返回键出现 ...

随机推荐

  1. SQL基本语句(1)

    利用select的结果创建表 可以通过选择一个表的全部内容(无 WHERE 子句)来拷贝一个表,或利用一个总是失败的 WHERE 子句来创建一个空表,如: mysql> CREATE TABLE ...

  2. 8051学习笔记——AD

    AD.C #include<reg52.h> #include <iic.h> #define PCF8591 0x90 //PCF8591 地址 sbit LS138A=P2 ...

  3. 操作笔记:linux下安装ftp

    1,安装ftp [root@iZ945sgm0ugZ ~]# yum install vsftpd 安装成功的信息: [root@iZ945sgm0ugZ ~]# yum install vsftpd ...

  4. .NET Reflector反编译的方法

    首先启动.NET Reflector,然后添加进入dll或exe.然后选择Export Source Code...,将反编译后的代码文件,生成到指定目录. 到这一步骤时,稍等一会,就能够在指定目录就 ...

  5. dig out secrets beneath AirSig

    My sister installed AirSig last week. She is so exciting about this new techknology and she won't st ...

  6. Android IOS WebRTC 音视频开发总结(四一)-- QQ和webrtc打洞能力pk

    很多人知道webrtc打洞能力很强,到底有多强但是不知道,比较好的方法就是跟QQ对比,但大多数公司很难模拟各种网络环境进行测试,比如联通,铁通,电信,移动,所以这次请小师妹在实验室下进行了一个比较全面 ...

  7. 根据JSON对象动态加载表格--大数据量

    EasyUI的DataGrid加载数据的时候,如果列数过多(300列以上),数据渲染及其缓慢. JSON对象格式: 1:rowno 2:title 3:colspan 4:rowspan 5:back ...

  8. Python之路【第五篇】:面向对象及相关

    Python之路[第五篇]:面向对象及相关   面向对象基础 基础内容介绍详见一下两篇博文: 面向对象初级篇 面向对象进阶篇 其他相关 一.isinstance(obj, cls) 检查是否obj是否 ...

  9. typedef 及其与struct的结合使用

    //相当于为现有类型创建一个别名,或称类型别名. //整形等 typedef int size; //字符数组 ]; ];//=> typedef ]; Line text, secondlin ...

  10. Oracle一些常用的查询命令总结(持续更新)

    更新于:2015年1月28日 17:08:13 -------------------------表空间 --------------------------------------- ----- 查 ...