安卓项目实战之优雅的退出APP的终极解决方案

前言

现在几乎所有的app都有退出程序的功能(最常见的就是在项目主Activity中连续点击两次返回按钮退出app)至于具体如何实现退出功能,网上有罗列出以下4种方式:

  1. 容器式
  2. SingleTask式
  3. 广播式
  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 List<Activity> activityList = new ArrayList<>();

    private ActivityManager() {
    }

    public static ActivityManager getInstance() {

        return ActivityManagerHolder.Instantce;
    }

    /**
     * 静态内部类获取单例
     */
    static class ActivityManagerHolder {
        public static ActivityManager Instantce = new ActivityManager();

    }

    /**
     * 添加activity
     * @param activity
     */
    public void addActivity(Activity activity){
        if (!activityList.contains(activity)) {
            activityList.add(activity);
        }

    }

    /**
     * 移除activity
     * @param activity
     */
    public void removeActivity(Activity activity){
        if (activityList.contains(activity)) {
            activityList.remove(activity);
        }
    }

    /**
     * 关闭所有的activity,退出应用
     */
    public void finishActivitys(){
        if (activityList != null&&!activityList.isEmpty()) {
            for (Activity activity1 : activityList) {
                activity1.finish();
            }
            activityList.clear();
        }
    }

}

BaseActivity中:

public class BaseActivity extends AppCompatActivity {

    private WeakReference<Activity> weakReference = null;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //将activity添加到列表中
        if (weakReference==null) {
            weakReference =  new WeakReference<Activity>(this);
        }
      ActivityManager.getInstance().addActivity(weakReference.get());

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //将activity从列表中移除
        ActivityManager.getInstance().removeActivity(weakReference.get());
    }
    
}

当需要退出应用的时候直接调用ActivityManager中的关闭Activity的方法即可,如下:

ActivityManager.getInstance().finishActivitys();

2.SingleTask式

该模式下又分为两种情况:

  1. 如果项目需求是在主Activity中实现退出app的功能 — 懒人式
  2. 项目需求在其他某一非主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的效果,但是这种太暴力了不推荐使用,而且用户体验不好,所以也不推荐使用。

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/82863331