Skip to content

实现路由跳转

Xia Shengming edited this page Feb 17, 2019 · 3 revisions

通过上节的介绍,我们知道在初始化的时候已经拿到了所有的路由信息,那么实现跳转便好做了。

@Route(path = "/main/main")
public class MainActivity extends AppCompatActivity {

  public void startModule1MainActivity(View view) {
    EasyRouter.getsInstance().build("/module1/module1main").navigation();
  }

}

在build的时候,传入要跳转的路由地址,build()方法会返回一个Postcard对象,我们称之为跳卡。然后调用Postcard的navigation()方法完成跳转。用过ARouter的对这个跳卡都应该很熟悉吧!Postcard里面保存着跳转的信息。下面我把Postcard类的代码实现粘下来:

public class Postcard extends RouteMeta {
    private Bundle mBundle;
    private int flags = -1;
    //新版风格
    private Bundle optionsCompat;
    //老版
    private int enterAnim;
    private int exitAnim;

    //服务
    private IService service;

    public Postcard(String path, String group) {
        this(path, group, null);
    }

    public Postcard(String path, String group, Bundle bundle) {
        setPath(path);
        setGroup(group);
        this.mBundle = (null == bundle ? new Bundle() : bundle);
    }

    public Bundle getExtras() {return mBundle;}

    public int getEnterAnim() {return enterAnim;}

    public int getExitAnim() {return exitAnim;}

    public IService getService() {
        return service;
    }

    public void setService(IService service) {
        this.service = service;
    }

    /**
     * Intent.FLAG_ACTIVITY**
     * @param flag
     * @return
     */
    public Postcard withFlags(int flag) {
        this.flags = flag;
        return this;
    }

    public int getFlags() {
        return flags;
    }

    /**
     * 跳转动画
     *
     * @param enterAnim
     * @param exitAnim
     * @return
     */
    public Postcard withTransition(int enterAnim, int exitAnim) {
        this.enterAnim = enterAnim;
        this.exitAnim = exitAnim;
        return this;
    }

    /**
     * 转场动画
     *
     * @param compat
     * @return
     */
    public Postcard withOptionsCompat(ActivityOptionsCompat compat) {
        if (null != compat) {
            this.optionsCompat = compat.toBundle();
        }
        return this;
    }

    public Postcard withString(@Nullable String key, @Nullable String value) {
        mBundle.putString(key, value);
        return this;
    }


    public Postcard withBoolean(@Nullable String key, boolean value) {
        mBundle.putBoolean(key, value);
        return this;
    }


    public Postcard withShort(@Nullable String key, short value) {
        mBundle.putShort(key, value);
        return this;
    }


    public Postcard withInt(@Nullable String key, int value) {
        mBundle.putInt(key, value);
        return this;
    }


    public Postcard withLong(@Nullable String key, long value) {
        mBundle.putLong(key, value);
        return this;
    }


    public Postcard withDouble(@Nullable String key, double value) {
        mBundle.putDouble(key, value);
        return this;
    }


    public Postcard withByte(@Nullable String key, byte value) {
        mBundle.putByte(key, value);
        return this;
    }


    public Postcard withChar(@Nullable String key, char value) {
        mBundle.putChar(key, value);
        return this;
    }


    public Postcard withFloat(@Nullable String key, float value) {
        mBundle.putFloat(key, value);
        return this;
    }


    public Postcard withParcelable(@Nullable String key, @Nullable Parcelable value) {
        mBundle.putParcelable(key, value);
        return this;
    }


    public Postcard withStringArray(@Nullable String key, @Nullable String[] value) {
        mBundle.putStringArray(key, value);
        return this;
    }


    public Postcard withBooleanArray(@Nullable String key, boolean[] value) {
        mBundle.putBooleanArray(key, value);
        return this;
    }


    public Postcard withShortArray(@Nullable String key, short[] value) {
        mBundle.putShortArray(key, value);
        return this;
    }


    public Postcard withIntArray(@Nullable String key, int[] value) {
        mBundle.putIntArray(key, value);
        return this;
    }


    public Postcard withLongArray(@Nullable String key, long[] value) {
        mBundle.putLongArray(key, value);
        return this;
    }


    public Postcard withDoubleArray(@Nullable String key, double[] value) {
        mBundle.putDoubleArray(key, value);
        return this;
    }


    public Postcard withByteArray(@Nullable String key, byte[] value) {
        mBundle.putByteArray(key, value);
        return this;
    }


    public Postcard withCharArray(@Nullable String key, char[] value) {
        mBundle.putCharArray(key, value);
        return this;
    }


    public Postcard withFloatArray(@Nullable String key, float[] value) {
        mBundle.putFloatArray(key, value);
        return this;
    }


    public Postcard withParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {
        mBundle.putParcelableArray(key, value);
        return this;
    }

    public Postcard withParcelableArrayList(@Nullable String key, @Nullable ArrayList<? extends
            Parcelable> value) {
        mBundle.putParcelableArrayList(key, value);
        return this;
    }

    public Postcard withIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
        mBundle.putIntegerArrayList(key, value);
        return this;
    }

    public Postcard withStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
        mBundle.putStringArrayList(key, value);
        return this;
    }

    public Bundle getOptionsBundle() {
        return optionsCompat;
    }

    public Object navigation() {
        return EasyRouter.getsInstance().navigation(null, this, -1, null);
    }

    public Object navigation(Context context) {
        return EasyRouter.getsInstance().navigation(context, this, -1, null);
    }


    public Object navigation(Context context, NavigationCallback callback) {
        return EasyRouter.getsInstance().navigation(context, this, -1, callback);
    }

    public Object navigation(Context context, int requestCode) {
        return EasyRouter.getsInstance().navigation(context, this, requestCode, null);
    }

    public Object navigation(Context context, int requestCode, NavigationCallback callback) {
        return EasyRouter.getsInstance().navigation(context, this, requestCode, callback);
    }


}

如果你是一个Android开发,Postcard类里面的东西就不用我再给你介绍了吧!(哈哈)我相信你一看就明白了。我们只介绍一个方法navigation(),他有好几个重载方法,方法里面会调用EasyRouter类的navigation()方法。EaseRouter的navigation()方法,就是跳转的核心了。下面请看:

protected Object navigation(Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        prepareCard(postcard);
    }catch (NoRouteFoundException e) {
        e.printStackTrace();
        //没找到
        if (null != callback) {
            callback.onLost(postcard);
        }
        return null;
    }
    if (null != callback) {
        callback.onFound(postcard);
    }

    switch (postcard.getType()) {
        case ACTIVITY:
            final Context currentContext = null == context ? mContext : context;
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());
            int flags = postcard.getFlags();
            if (-1 != flags) {
                intent.setFlags(flags);
            } else if (!(currentContext instanceof Activity)) {
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    //可能需要返回码
                    if (requestCode > 0) {
                        ActivityCompat.startActivityForResult((Activity) currentContext, intent,
                                requestCode, postcard.getOptionsBundle());
                    } else {
                        ActivityCompat.startActivity(currentContext, intent, postcard
                                .getOptionsBundle());
                    }

                    if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) &&
                            currentContext instanceof Activity) {
                        //老版本
                        ((Activity) currentContext).overridePendingTransition(postcard
                                        .getEnterAnim()
                                , postcard.getExitAnim());
                    }
                    //跳转完成
                    if (null != callback) {
                        callback.onArrival(postcard);
                    }
                }
            });
            break;
        case ISERVICE:
            return postcard.getService();
        default:
            break;
    }
    return null;
}

这个方法里先去调用了prepareCard(postcard)方法,prepareCard(postcard)代码我贴出来,

private void prepareCard(Postcard card) {
    RouteMeta routeMeta = Warehouse.routes.get(card.getPath());
    if (null == routeMeta) {
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(card.getGroup());
        if (null == groupMeta) {
            throw new NoRouteFoundException("没找到对应路由:分组=" + card.getGroup() + "   路径=" + card.getPath());
        }
        IRouteGroup iGroupInstance;
        try {
            iGroupInstance = groupMeta.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("路由分组映射表记录失败.", e);
        }
        iGroupInstance.loadInto(Warehouse.routes);
        //已经准备过了就可以移除了 (不会一直存在内存中)
        Warehouse.groupsIndex.remove(card.getGroup());
        //再次进入 else
        prepareCard(card);
    } else {
        //类 要跳转的activity 或IService实现类
        card.setDestination(routeMeta.getDestination());
        card.setType(routeMeta.getType());
        switch (routeMeta.getType()) {
            case ISERVICE:
                Class<?> destination = routeMeta.getDestination();
                IService service = Warehouse.services.get(destination);
                if (null == service) {
                    try {
                        service = (IService) destination.getConstructor().newInstance();
                        Warehouse.services.put(destination, service);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                card.setService(service);
                break;
            default:
                break;
        }
    }
}

注意,Warehouse就是专门用来存放路由映射关系的类,这在ARouter里面也是。这段代码Warehouse.routes.get(card.getPath())通过path拿到对应的RouteMeta,这个RouteMeta里面保存了activityClass等信息。继续往下看,如果判断拿到的RouteMeta是空,说明这个路由地址还没有加载到map里面(为了效率,这里是用了懒加载),只有在第一次用到当前路由地址的时候,会去Warehouse.routes里面拿routeMeta,如果拿到的是空,会根据当前路由地址的group拿到对应的分组,通过反射创建实例,然后调用实例的loadInfo方法,把它里面保存的映射信息添加到Warehouse.routes里面,并且再次调用prepareCard(card),这时再通过Warehouse.routes.get(card.getPath())就可以顺利拿到RouteMeta了。进入else{}里面,调用了card.setDestination(routeMeta.getDestination()),这个setDestination就是将RouteMeta里面保存的activityClass放入Postcard里面,下面switch代码块可以先不用看,这是实现ARouter中通过依赖注入实现Provider 服务的逻辑,有心研究的同学可以去读一下demo。
好了,prepareCard()方法调用完成后,我们的postcard里面就保存了activityClass,然后switch (postcard.getType()){}会判断postcard的type为ACTIVITY,然后通过ActivityCompat.startActivity启动Activity。到这里,路由跳转的实现已经讲解完毕了。