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<Activity> 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<Activity> 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<Activity> 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<String> sAddTimeList = new ArrayList<>();

    private static List<Class<Activity>> 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<Activity> 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<Activity> 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<Activity> 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;
    }

最后

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容