前言
现在几乎所有的app都有退出程序的功能(最常见的就是在项目主Activity中连续点击两次返回按钮退出app)至于具体如何实现退出功能,网上有罗列出以下4种方式:
- 容器式
- SingleTask式
- 广播式
- 进程式
个人比较推荐使用弱引用的容器式,应该也是目前使用最多的方式,在SingleTask式中本人又根据具体的项目需求进行了细分,有在主Activity实现退出功能的懒人式,和在非主Activity中实现退出功能的通用式,基本涵盖了大多数app退出功能的实现需求,具体如何实现请耐心往下看。
下面我们一一来进行介绍:
1.容器式+弱引用方式
容器式可能是我们最常见的方式之一了,主要通过创建一个全局的容器,把所有的Activity都保存下来,退出的时候循环遍历所有activity,然后finish()掉,通常我们写法如下:
BaseActivity中:
public abstract class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 添加Activity到堆栈
ActivityUtils.getInstance().addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 结束Activity&从栈中移除该Activity
ActivityUtils.getInstance().removeActivity(this);
}
}
对应的全局的Activity的管理类如下:
public class ActivityUtils{
private ActivityUtils() {
}
private static AtyContainer instance = new AtyContainer();
private static List<Activity> activitys = new ArrayList<Activity>();
public static ActivityUtils getInstance() {
return instance;
}
public void addActivity(Activity activity) {
activityStack.add(activity);
}
public void removeActivity(Activity activity) {
activityStack.remove(activity);
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
activityStack.get(i).finish();
}
}
activityStack.clear();
}
}
这种方法比较简单, 但是可以看到ActivityUtils持有着Activity的强引用,也就是说当某个Activity异常退出时,如果ActivityUtils没有及时释放掉引用,就会导致内存问题,而且代码量多,不够优雅,诸多不便。
针对以上容器式实现存在的内存问题,我们可以使用弱引用的方式来进行避免,具体实现如下:
全局的Activity管理类:
public class ActivityManager {
private Stack<WeakReference<Activity>> mActivityStack;
private static volatile ActivityManager mInstance = new ActivityManager();
private ActivityManager() {
}
public static ActivityManager getInstance() {
if (mInstance == null) {
synchronized (ActivityManager.class) {
if (mInstance == null) {
mInstance = new ActivityManager();
}
}
}
return mInstance;
}
/**
* 入栈
* @param activity activity实例
*/
public void addActivity(Activity activity) {
if (mActivityStack == null) {
mActivityStack = new Stack<>();
}
mActivityStack.add(new WeakReference<>(activity));
}
/**
* 出栈
* @param activity
*/
public void removeActivity(Activity activity) {
if (mActivityStack != null) {
mActivityStack.remove(new WeakReference<>(activity));
}
}
/**
* 检查弱引用是否释放,若释放,则从栈中清理掉该元素
*/
public void checkWeakReference() {
if (mActivityStack != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity temp = activityReference.get();
if (temp == null) {
it.remove();// 使用迭代器来进行安全的加锁操作
}
}
}
}
/**
* 获取当前Activity
* @return 当前(栈顶)activity
*/
public Activity currentActivity() {
checkWeakReference();
if (mActivityStack != null && !mActivityStack.isEmpty()) {
return mActivityStack.lastElement().get();
}
return null;
}
/**
* 结束除当前activtiy以外的所有activity
* @param activtiy 不需要结束的activity
*/
public void finishOtherActivity(Activity activtiy) {
if (mActivityStack != null && activtiy != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity temp = activityReference.get();
if (temp == null) {
// 清理掉已经释放的activity
it.remove();
continue;
}
if (temp != activtiy) {
// 使用迭代器来进行安全的加锁操作
it.remove();
temp.finish();
}
}
}
}
/**
* 结束除指定activtiy以外的所有activity
* @param cls 指定的某类activity
*/
public void finishOtherActivity(Class<?> cls) {
if (mActivityStack != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity activity = activityReference.get();
if (activity == null) {
// 清理掉已经释放的activity
it.remove();
continue;
}
if (!activity.getClass().equals(cls)) {
// 使用迭代器来进行安全的加锁操作
it.remove();
activity.finish();
}
}
}
}
/**
* 结束当前Activity
*/
public void finishActivity() {
Activity activity = currentActivity();
if (activity != null) {
finishActivity(activity);
}
}
/**
* 结束指定的Activity
* @param activity 指定的activity实例
*/
public void finishActivity(Activity activity) {
if (activity != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity temp = activityReference.get();
if (temp == null) {
// 清理掉已经释放的activity
it.remove();
continue;
}
if (temp == activity) {
it.remove();
}
}
activity.finish();
}
}
/**
* 结束指定类名的所有Activity
* @param cls 指定的类的class
*/
public void finishActivity(Class<?> cls) {
if (mActivityStack != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity activity = activityReference.get();
if (activity == null) {
// 清理掉已经释放的activity
it.remove();
continue;
}
if (activity.getClass().equals(cls)) {
it.remove();
activity.finish();
}
}
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
if (mActivityStack != null) {
for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {
WeakReference<Activity> activityReference = it.next();
Activity activity = activityReference.get();
if (activity != null) {
activity.finish();
}
}
mActivityStack.clear();
}
}
}
BaseActivity中:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 添加进全局的activity管理栈
ActivityManager.getInstance().addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//将该activity从栈中移除
ActivityManager.getInstance().removeActivity(this);
}
}
当需要退出应用的时候直接调用ActivityManager中的关闭Activity的方法即可,如下:
销毁指定的activity:ActivityManager.getInstance().finishActivity()
退出应用调用 ActivityManager.getInstance().finishAllActivity()
2.SingleTask式
该模式下又分为两种情况:
- 如果项目需求是在主Activity中实现退出app的功能 — 懒人式
- 项目需求在其他某一非主Activity的页面实现退出功能 — 通用式
我们先来回顾下Activity的四种加载模式中的SingleTask模式:
我们知道Activity有四种加载模式,而singleTask就是其中的一种,使用这个模式之后,当startActivity时,它先会在当前栈中查询是否存在Activity的实例,如果存在,则将其至于栈顶,并将其之上的所有Activity移除栈。我们打开一个app,首先是一个splash页面,然后会finish掉splash页面。跳转到主页。然后会在主页进行N次的跳转,期间会产生数量不定的Activity,有的被销毁,有的驻留在栈中,但是栈底永远是我们的HomeActivity。
懒人式的具体实现
根据上面对SingleTask加载模式的分析可知,如果我们项目需求是要在HomeActivity即主Activity中实现退出功能,那么我们仅仅只需简单的两步:
1.首先在主Activity添加具体的退出代码:
private boolean isExit;
/**
* 双击返回键退出
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isExit) {
this.finish();
} else {
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
isExit = true;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
isExit= false;
}
}, 2000);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
2.在清单文件中设置主Activity的加载模式为SingleTask:
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />
通用式的具体实现
对于以SingleTask加载的Activity有如下特点:
假设我们的MainActivity是使用的SingleTask的启动模式,假设我跳转到了其他的页面,然后使用startActivity(this,MainActivity.class)的方式再次启动MainActivity,这时MainActivity走到onNewIntent()方法,然后按照生命周期onRestart()——>onStart()——>onResume(),MainActivity不会重新创建。
根据以上特点,我们就可以分三步实现在非主Activity中退出activity的功能:
假如我们从主界面MainActivity跳转到了FirstActivity,又从FirstActivity跳转到了SecondActivity,现在需要在SecondActivity中实现退出app的功能:
1.设置MainActivity的加载模式为SingleTask:
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />
2.实现MainActivity中的onNewIntent方法,实现退出APP的逻辑:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String tag = intent.getStringExtra("EXIT_TAG");
if (tag != null&& !TextUtils.isEmpty(tag)) {
if ("SINGLETASK".equals(tag)) {//退出程序
finish();
}
}
}
注意:此处实现的是MainActivity中的onNewIntent方法,而非SecondActivity中的。
3.第二步里面的tag用来标识关闭app的,在secondActivity中退出APP调用startActivity的时候需要传入。
secondActivity中相关代码如下:
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("EXIT_TAG", "SINGLETASK");
startActivity(intent);
通过以上三步就解决了在非主Activity优雅安全的退出APP的问题。
3.广播式
BaseActivity中代码如下:
public abstract class BaseActivity extends Activity {
private static final String ACTION = "action.exit";
private ExitReceiver exitReceiver = new ExitReceiver();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION);
registerReceiver(exitReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(exitReceiver);
}
class ExitReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BaseActivity.this.finish();
}
}
}
然后只需要在想要退出的地方,发送一个广播,注意Action和注册时的相同就可以了。
但是个人觉得这种方式还是太耗性能,毕竟广播是进程间通信,我们一个退出APP功能不是特别的有必要,因此不建议使用。
4.进程式
1.android.os.Process.killProcess(android.os.Process.myPid());
2.System.exit(0);
3.ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
manager.killBackgroundProcesses(getPackageName());
以上三种方式都能够达到杀死进程,直接退出APP的效果,但是这种太暴力了不推荐使用,而且用户体验不好,所以也不推荐使用。