Activity跳转实现“从哪儿来回哪去”

标题中引号的内容,相信各位Android程序员会碰到这样的需求。特别是当一个功能有多个入口的时候,更种跳转会让人抓狂。有时候我们会利用Activity的SingleTask模式来完成清栈操作,但当业务场景变的复杂的时候,就需要我们考虑其他方式了。

情景一

Activity的跳转路径为:
A->B->C->D->E->A。
最终要求存在栈里面的Activity只有A。

实现

通过设置Activity A为SingleTask模式可以完成该跳转操作(当然前提是要求所有Activity在同一个Task里面)。同时,我们也可以通过自定义一个Activity栈来完成该操作。代码如下:

import android.app.Activity;

import java.util.ArrayList;

/**
 * help to manager activity stack
 * @author kisson
 */
public class ActivityStackManager {

    private static ArrayList sActivityList = new ArrayList<>();

    private static class ActivityStackManagerHolder {
        private static ActivityStackManager sInstance = new ActivityStackManager();
    }

    public static ActivityStackManager getInstance() {
        return ActivityStackManagerHolder.sInstance;
    }

    public void addActivity(Activity activity) {
        sActivityList.add(activity);
    }

    /**
     * back to target activity
     *
     * @param addTime the add time of target activity
     * @return true if back to target activity successfully
     */
    public boolean back2TargetActivity(String addTime) {
        if (isTargetActivityExist(addTime)) {
            for (int i = sActivityList.size() - 1; i >= 0; i--) {
                String var = ((BaseActivity) sActivityList.get(i)).getAddTime();
                if (!var.equals(addTime)) {
                    popActivityFromStack(sActivityList.get(i));
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * back to target activity
     *
     * @param indexActivityClass the class of target activity
     * @return true if back to target activity successfully
     */
    public boolean back2TargetActivity(Class indexActivityClass) {
        if (isTargetActivityExist(indexActivityClass)) {
            for (int i = sActivityList.size() - 1; i >= 0; i--) {
                if (sActivityList.get(i).getClass() != indexActivityClass) {
                    popActivityFromStack(sActivityList.get(i));
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isTargetActivityExist(String addTime) {
        for (Activity activity : sActivityList) {
            if(activity == null){
                continue;
            }
            if (((BaseActivity) activity).getAddTime().equals(addTime)) {
                return true;
            }
        }
        return false;
    }

    private boolean isTargetActivityExist(Class targetActivityClass) {
        for (Activity activity : sActivityList) {
           if(activity == null){
                continue;
            }
            if (activity.getClass() == targetActivityClass) {
                return true;
            }
        }
        return false;
    }

    private void popActivityFromStack(Activity activity) {
        if (activity != null && !activity.isFinishing()) {
            activity.finish();
            sActivityList.remove(activity);
        }
    }

    public void removeActivity(Activity activity) {
        sActivityList.remove(activity);
    }

}

以上写的Activity栈管理器比较简单,我们可以根据需求进行拓展。接着定义一个BaseActivity类,来完成入栈和出栈等相关操作,代码如下。

import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;

/**
 * Created by kisson on 16/6/7.
 */
public class BaseActivity extends Activity {

    private String addTime;

    public String getAddTime() {
        return addTime;
    }

    public void setAddTime(String addTime) {
        this.addTime = addTime;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setAddTime(String.valueOf(SystemClock.currentThreadTimeMillis()));
        ActivityStackManager.getInstance().addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityStackManager.getInstance().removeActivity(this);
    }
}

这里在BaseActivity中定义了addTime变量,那么它的作用什么?
比如Activity的跳转路径为:
A->B->C->D->B'->E。
在该跳转路径上Activity B 出现两次(Activity B的启动模式为standard),虽然它们是同一个Activity,但是是不同的的实例,因此通过Class来区分就显得不够用了。所以,在这里添加addTime变量,用于保证Activity实例的唯一性。
接着,我们需要实现一个辅助类用于完成“情景一”的跳转。

package com.dighammer.kisson.goback;

import android.app.Activity;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by kisson on 16/6/8.
 */
public class ActivityPathManager {

    /**
     * 这里用数组记录源Activity而不是单独用一个String,是因为:在某一个跳转路径上可能有多个注册源Activity行为
     * 比如A->B->C-D-A,在A中进行注册源Activity,
     * 同时另外一条链路M->N->B->C->D->M(当然这两条链路是不可能同时发生的),需要在B中注册源Activity。但是这两条链路有重合部分,
     * 如果仅仅用String来表示addTime,会存在覆盖的情况,因此用数组来保存addTime,但是只有第一条数据有效。
     **/
    private static List sAddTimeList = new ArrayList<>();

    private static List> sActivityClassList = new ArrayList<>();

    private static class ActivityPathManagerHolder {
        private static ActivityPathManager sInstance = new ActivityPathManager();
    }

    public static ActivityPathManager getInstance() {
        return ActivityPathManagerHolder.sInstance;
    }

    /**
     * 注册源Activity
     *
     * @param addTime Activity的创建时间,可以唯一表示某一Activity
     */
    public void registerSourceActivity(String addTime) {
        sAddTimeList.add(addTime);
    }

    /**
     * 注册源Activity
     *
     * @param indexClass Activity的类名
     */
    public void registerSourceActivity(Class indexClass) {
        sActivityClassList.add(indexClass);
    }

    /**
     * 当从源Activity通过任意跳转路径到达目标Activity时,调用此方法后可以返回到源Activity
     *
     * @return 如果是true,直接跳转到源Activity;如果是false,走原有逻辑
     */
    public boolean back2SourceActivity() {
        if (!sAddTimeList.isEmpty()) {
            ActivityStackManager.getInstance().back2TargetActivity(sAddTimeList.get(0));
            clearAddTime();
            return true;
        }
        return false;
    }

    /**
     * 当从源Activity通过任意跳转路径到达目标Activity时,调用此方法后可以返回到源Activity
     *
     * @return 如果是true,直接跳转到源Activity;如果是false,走原有逻辑
     */
    public boolean back2SourceActivity2() {
        if (!sActivityClassList.isEmpty()) {
            ActivityStackManager.getInstance().back2TargetActivity(sActivityClassList.get(0));
            clearClass();
            return true;
        }
        return false;
    }

    /**
     * 当从源Activity通过任意跳转路径到达目标Activity时,调用此方法后可以返回到源Activity,此方法不需要注册Activity
     *
     * @param addTime 源Activity的添加时间
     * @return 如果是true,直接跳转到源Activity;如果是false,走原有逻辑
     */
    public boolean back2SourceActivity(String addTime) {
        if (addTime != null) {
            ActivityStackManager.getInstance().back2TargetActivity(addTime);
            return true;
        }
        return false;
    }

    /**
     * 当从源Activity通过任意跳转路径到达目标Activity时,调用此方法后可以返回到源Activity,此方法不需要注册Activity
     *
     * @param indexClass 源Activity的类
     * @return 如果是true,直接跳转到源Activity;如果是false,走原有逻辑
     */
    public boolean back2SourceActivity(Class indexClass) {
        if (indexClass != null) {
            ActivityStackManager.getInstance().back2TargetActivity(indexClass);
            return true;
        }
        return false;
    }


    private void clearAddTime() {
        sAddTimeList.clear();
    }

    private void clearClass() {
        sActivityClassList.clear();
    }

    /**
     * 清除所有已经注册Activity的addTime
     * notice:在你的源Activity的onCreate和onRestart方法调用该方法!
     */
    public void unregisterSourceActivity(String addTime) {
        sAddTimeList.remove(addTime);
    }

    /**
     * 清除所有已经注册Activity的Class
     * notice:在你的源Activity的onCreate和onRestart方法调用该方法!
     */
    public void unregisterSourceActivity(Class indexClass) {
        sActivityClassList.remove(indexClass);
    }
}

用法

在源Activity中通过调用ActivityPathManager的registerSourceActivity方法进行注册(注意在源Activity的onCreate和onRestart方法进行注销),比如。

    public void onClick(View view) {
        startActivity(new Intent(SourceActivity.this, AActivity.class));
        ActivityPathManager.getInstance().registerSourceActivity(getAddTime());
    }

在最终跳转到的目标Activity通过调用ActivityPathManager的back2SourceActivity方法返回到源Activity,比如。

    public void onClick(View view){
        ActivityPathManager.getInstance().back2SourceActivity();
    }

最终的结果可以成功返回到源Activity。

情景二

本文所实现的“从哪来回哪去”功能并不能顾及到所有跳转情况,但是我们可以根据需求在此基础上进行拓展。
比如跳转路径为A->B->C->D->E->F。
最终要求栈里只有A和F,并且F后退是返回到A的。
这种跳转需求,Activity的四种启动模式就无法搞定了。
但是我们可以增加ActivityPathManager和ActivityStackManager类中的方法,来完成相应的功能。
在ActivityStackManager类中增加back2TargetActivityExceptTop方法,代码如下。

    /**
     * back to target activity but do not pop the top activity
     *
     * @param addTime the add time of target activity
     * @return true if back to target activity successfully
     */
    public boolean back2TargetActivityExceptTop(String addTime) {
        if (isTargetActivityExist(addTime)) {
            for (int i = sActivityList.size() - 2; i >= 0; i--) {
                String var = ((BaseActivity) sActivityList.get(i)).getAddTime();
                if (!var.equals(addTime)) {
                    popActivityFromStack(sActivityList.get(i));
                } else {
                    return true;
                }
            }
        }
        return false;
    }

通用在ActivityPathManager类中增加back2SourceActivityExceptTop方法,代码如下:

    public boolean back2SourceActivityExceptTop() {
        if (!sAddTimeList.isEmpty()) {
            ActivityStackManager.getInstance().back2TargetActivityExceptTop(sAddTimeList.get(0));
            clearAddTime();
            return true;
        }
        return false;
    }

最后

欢迎大家来提出宝贵意见,或者某些情景下,本文功能无法实现的!

你可能感兴趣的:(Activity跳转实现“从哪儿来回哪去”)