前言
查看了太多网上错误的例子,因此想借此经验让大家明白优雅的退出APP的过程。
1.功能需求
公司要开发一款android APP,要求能按系统的‘退出键’进行退出,退出键退出很简单。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
ActivityManager.getInstance().exitApp();
}
}, 500);
return true;
}
return super.onKeyDown(keyCode, event);
}
KeyEvent.KEYCODE_BACK,表示用户按了系统返回键,
return true;表示拦截了该事件,不再向下执行系统的super.onKeyDown(keyCode, event);逻辑,当然如果业务需要,可以继续执行系统逻辑。
ActivityManager.getInstance().exitApp();
package com.yys.utils;
import android.app.Activity;
import android.app.Application;
import java.lang.ref.WeakReference;
import java.util.Stack;
/**
* Activity管理类
* <p>
* 添加/删除 建议在{@link Application#registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)}中统一处理
* (此方法比在BaseActivity中处理要好)
*/
public class ActivityManager {
private static Stack<WeakReference<Activity>> mActStack = new Stack<>();
private static class Singleton {
private static final ActivityManager INSTANCE = new ActivityManager();
}
public static ActivityManager getInstance() {
return Singleton.INSTANCE;
}
private ActivityManager() {
}
/*** 添加 建议在Application中统一处理 */
public void add(Activity activity) {
mActStack.add(new WeakReference<>(activity));
}
/*** 移除 建议在Application中统一处理 */
public void remove(Activity activity) {
for (WeakReference<Activity> temp : mActStack) {
if (isEqualsActivity(temp, activity)) {
mActStack.remove(temp);
break;
}
}
}
/**
* 获取当前Activity数量
*
* @return
*/
public int getCount() {
return mActStack.size();
}
/**
* 栈内是否包含此activity
*
* @param cls
* @return
*/
public boolean isContains(Class<?> cls) {
for (WeakReference<Activity> temp : mActStack) {
if (isEqualsActivity(temp, cls)) {
return true;
}
}
return false;
}
/**
* 查找指定Activity 默认第一个
*
* @param cls
* @return 未找到则返回 null
*/
public <T extends Activity> T find(Class cls) {
return findFirst(cls);
}
/**
* 查找指定Activity 第一个
*
* @param cls
* @return
*/
public <T extends Activity> T findFirst(Class cls) {
for (WeakReference<Activity> temp : mActStack) {
if (isEqualsActivity(temp, cls)) {
return (T) temp.get();
}
}
return null;
}
/**
* 查找指定Activity 最后一个
*
* @param cls
* @return
*/
public <T extends Activity> T findLast(Class cls) {
for (int i = mActStack.size() - 1; i >= 0; i--) {
WeakReference<Activity> temp = mActStack.get(i);
if (isEqualsActivity(temp, cls)) {
return (T) temp.get();
}
}
return null;
}
/**
* 获取当前(即最后一个) Activity
*
* @return
*/
public <T extends Activity> T getCurrent() {
if (mActStack.lastElement() != null) {
return (T) mActStack.lastElement().get();
}
return null;
}
private boolean isEqualsActivity(WeakReference<Activity> temp, Class cls) {
if (temp != null && temp.get() != null && temp.get().getClass().equals(cls)) {
return true;
}
return false;
}
private boolean isEqualsActivity(WeakReference<Activity> temp, Activity activity) {
if (temp != null && temp.get() != null && temp.get() == activity) {
return true;
}
return false;
}
/**
* 结束指定Activity
*
* @param activity
*/
public void finish(Activity activity) {
if (activity != null && !activity.isFinishing()) {
activity.finish();
}
// remove(activity);//这个可以不用的 记得在Application中调用
}
/**
* 结束指定Activity
* 注:当栈中可能包含多个该Activity时,该方法会将所有的该Activity都finish
*
* @param cls
*/
public void finish(Class cls) {
for (WeakReference<Activity> temp : mActStack) {
if (isEqualsActivity(temp, cls)) {
finish(temp.get());
}
}
}
/**
* 结束此Activity之前的所有Activity(不包括当前的 ,结束当前需手动) 最终显示此Activity
* 1-2-3 * 4-5
* 1-2 * 4-5
* 1 * 4-5
* * 4-5
*
* @param cls
*/
public void finishBefore(Class cls) {
boolean isFound = false;
for (int i = mActStack.size() - 1; i >= 0; i--) {
WeakReference<Activity> temp = mActStack.get(i);
if (isFound) {
if (temp != null && temp.get() != null) {
finish(temp.get());
}
} else if (isEqualsActivity(temp, cls)) {
isFound = true;
}
}
}
/**
* 结束此Activity之后的所有Activity(不包括当前的 ,结束当前需手动) 最终显示此Activity
* 1-2-3 * 4-5
* 1-2-3 * 5
* 1-2-3 *
*
* @param cls
*/
public void finishAfter(Class cls) {
boolean isFound = false;
for (WeakReference<Activity> temp : mActStack) {
if (isFound) {
if (temp != null && temp.get() != null) {
finish(temp.get());
}
} else if (isEqualsActivity(temp, cls)) {
isFound = true;
}
}
}
public void finishAll() {
for (int i = 0; i < mActStack.size(); i++) {
if (mActStack.size() > 0 && mActStack.get(i) != null) {
mActStack.peek();
finish(mActStack.get(i).get());
i--;//删除完后索引减1,保证集合正常遍历
}
}
}
public void exitApp() {
finishAll();
}
}
重点方法finishAll()
(1)这里在重申一下for循环的执行逻辑
1.int i = 0;
2.i < mActStack.size();
3.{循环语句块}
4.i++;
5.i < mActStack.size();
6.{循环语句块}
7..i++;
... ...
当不满足条件.i < mActStack.size();时,语句退出
(2)问题点在于Activity栈Stack,是一个集合,在集合中移除对象或添加对象会导致集合ConcurrentModificationException
原因:
这是并发修改异常错误,经过网上搜索了解到,原来是集合遍历原理导致的,具体原因是这样的:不管是哪种方式的集合遍历方法,当我们在遍历某个集合的时候,Collection的实现并没有同步化,如果在多线程应用程序中出现同时访问,而且出现修改操作的时候都要求外部操作同步化;调用遍历操作获得的遍历对象在多线程修改集合的时候也自动失效,并抛出java.util.ConcurrentModificationException。这种实现机制是fail-fast,对外部 的修改并不能提供任何保证。遍历对象在被创建的时候,同时创建了一张单链的索引表,指针指向原始数据对象,只能顺序读取,不能逆向操作,而set、list等集合是动态、可变的数据结构;当原始对象改变时,索引并为改变,因此,索引指针继续移动的时候,找不到要迭代的对象就会报错。
(3)i--;//删除完后索引减1,保证集合正常遍历,当每次执行完一个Activity退出后,初始索引又回到0,保证每次只取栈第一个的Activity调用finish。
(4)判断 mActStack.size() > 0也很关键,不然java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0,原因
因为finish调用界面销毁并不是立即执行完成的,mActStack可能还存在元素但是正在销毁,当销毁完成后,集合自然也就为空集合了,这时候再取0的元素自然会报错。
自测可用。
————————————————
原文链接:https://blog.csdn.net/baidu_30084597/article/details/108598915