WMRouter路由源码解析

本次阅读 WMRouter 源码,主要为了弄清楚一下两个问题:

  • 路由的注册

    框架如何将我们配置的路由扫描注册并集中管理

  • 路由过程

    通过 Router.startUri 如何实现一次路由

0、WMRouter接入

  • 基础库
  • 使用注解的库
  • gradle插件
  • 混淆规则
  • 1.2.1版本注意替换包名
  • Application 初始化
// 创建RootHandler
DefaultRootUriHandler rootHandler = new DefaultRootUriHandler(context);
// 初始化
Router.init(rootHandler);
复制代码

路由过程

1、路由处理器 & 路由注册

1.1、UriHandler

对某一类的Uri进行处理。提供 shouldHandle 抽象方法供子类重写来判断自己能否处理当前 UriRequest;提供

handleInternal 抽象方法供子类实现具体的处理逻辑。UriHandle 自身提供了 handle 方法,规定了所有 Handler

的基本逻辑:首先调用 shouldHandle 方法判断当前 Handler 是否需要处理;接着判断当前 Handler 是否配置了拦截器,如果有拦截器先交给拦截器处理,否则直接调用 handleInternal 进行处理。

public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
    if (shouldHandle(request)) {
        Debugger.i("%s: handle request %s", this, request);
        if (mInterceptor != null && !request.isSkipInterceptors()) {
            mInterceptor.intercept(request, new UriCallback() {
                @Override
                public void onNext() {
                    handleInternal(request, callback);
                }
                @Override
                public void onComplete(int result) {
                    callback.onComplete(result);
                }
            });
        } else {
            handleInternal(request, callback);
        }
    } else {
        Debugger.i("%s: ignore request %s", this, request);
        callback.onNext();
    }
}
复制代码

1.2、DefaultRootUriHandler

在 Appliction 中注册的默认的根路由处理器,从上面提供的图中也可以看出

1.3、PageAnnotationHandler

继承 PathHandler(继承 UriHandler)

  • shouldHandle

    • 只有uri格式为 wm_router://page/xxxx 时才进行处理
  • handle 方法,复写的 handle 方法,调用 ensureInit 进行初始化,后续逻辑仍然是 UriHandler中的handle的逻辑

    • ensureInit 通过 LazyInitHelper 实现
    • ServiceLoaderInit (Gradle插件生成)扫描所有的 ServiceInit_XXX 并调用 init 方法将 PageAnnotationInit_XXX & UriAnnotationInit_XXX 通过 ServiceLoader 注册
    • ensureInit 则是获取到上面注册的 XXXAnnotation_XXX 实现类,并调用 init 方法实现对应页面 Handler 的注册
  • handlerInteral

public static final String SCHEME = "wm_router";
public static final String HOST = "page";
public static final String SCHEME_HOST = RouterUtils.schemeHost(SCHEME, HOST);
@Override
protected boolean shouldHandle(@NonNull UriRequest request) {
    return SCHEME_HOST.matches(request.schemeHost());
}

@Override
public void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {
    mInitHelper.ensureInit();
    super.handle(request, callback);
}

private final LazyInitHelper mInitHelper = new LazyInitHelper("PageAnnotationHandler") {
    @Override
    protected void doInit() {
        initAnnotationConfig();
    }
};

// 获取 IPageAnnotationInit 的所有实现类 (Apt生成的)
// 生成的 PageAnnotationInit_XXX 类型
protected void initAnnotationConfig() {
    RouterComponents.loadAnnotation(this, IPageAnnotationInit.class);
}

public static <T extends UriHandler> void loadAnnotation(T handler, Class<? extends AnnotationInit<T>> initClass) {
    sAnnotationLoader.load(handler, initClass);
}
// sAnnotationLoader 对应的类
public class DefaultAnnotationLoader implements AnnotationLoader {

    public static final AnnotationLoader INSTANCE = new DefaultAnnotationLoader();

    @Override
    public <T extends UriHandler> void load(T handler,
            Class<? extends AnnotationInit<T>> initClass) {
        // ServiceLoader:获取对应接口的所有实现类
        List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass);
        // 调用对应的 Init 方法
        for (AnnotationInit<T> service : services) {
            service.init(handler);
        }
    }
}


public class PageAnnotationInit_bfd2eeb94778305f7d7586fccf7b2630 implements IPageAnnotationInit {
  public void init(PageAnnotationHandler handler) {
    handler.register("/fragment/kroom/feed", new FragmentTransactionHandler("com.netease.cloudmusic.singroom.feed.ui.fragments.SingRoomFeedFragment"));
  }
}
复制代码

PathHandler

根据path分发URI给子节点,支持注册的子节点包括ActivityClassName, ActivityClass, UriHandler (官方注释)

  • scheme + host:对应一个 PathHandler
  • PathHandler 内部的 mMap 记录了对应的 path 和 Handler (init的时候调用生成的字节码注册进来的)

UriAnnotationHandler

StartUriHandler

1.4、UriAnnotationHandler

实现和 PageAnnotationHandler 类似,支持不同的 scheme,内部构造了一个 PathHandler 来完成注册。和 PageAnnotationHandler 不同的是,PageAnnotationHandler 本身就是一个 PathHandler 类型。

public static UriHandler parse(Object target, int priority, Map<String, Integer> params,
                               Class<? extends UriInterceptor>... interceptors) {
    UriHandler handler = toHandler(target, params);
    if (handler != null) {
        if (UriSourceTools.FROM_INTERNAL == priority) {
            handler.addInterceptor(SourceInternalInterceptor.class);
        } else if (UriSourceTools.FROM_EXTERNAL == priority) {
            handler.addInterceptor(SourceExternalInterceptor.class);
        } else if (UriSourceTools.FROM_WEBVIEW == priority) {
            handler.addInterceptor(SourceWebViewInterceptor.class);
        }
        handler.addInterceptors(interceptors);
    }
    return handler;
}

private static UriHandler toHandler(Object target, Map<String, Integer> params) {
    if (target instanceof UriHandler) {
        return (UriHandler) target;
    } else if (target instanceof String) {
        // 一般代码中生成的注册方法都是对应这个case
        return new ActivityClassNameHandler((String) target, params);
    } else if (target instanceof Class) {
        Class clazz = (Class) target;
        if (isValidActivityClass(clazz)) {
            //noinspection unchecked
            return new ActivityHandler((Class<? extends Activity>) target, params);
        } else if (isValidUriHandlerClass(clazz)) {
            try {
                if (isSingleInstance(clazz)) {
                    return (UriHandler) SingletonPool.get(clazz, null);
                } else {
                    return (UriHandler) clazz.newInstance();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    return null;
}
复制代码

1.5、 Fragment 和 Activity 对应的 Handler

  • FragmentTransactionHandler

由PageAnnotationHandler在加载由注解处理器生成的 PageAnnotationInit_xxxxxx 时注册的

  • ActivityClassNameHandler

由UriAnnotationHandler在加载由注解处理器生成的UriAnnotationInit_xxxxxx 时注册的

1.6、Gradle 插件生成初始化注册代码

WMRouterTransform

public void transform(TransformInvocation invocation) {
    WMRouterLogger.info(TRANSFORM + "start...");
    long ms = System.currentTimeMillis();
    //非增量编译,先清空输出目录
    if (!invocation.isIncremental()) {
        try {
            invocation.getOutputProvider().deleteAll();
        } catch (IOException e) {
            WMRouterLogger.fatal(e);
        }
    }
    Set<String> initClasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
    Set<String> deleteClasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
    BaseTransform baseTransform = new BaseTransform(invocation, new TransformCallBack() {
        @Nullable
        @Override
        public byte[] process(@NotNull String className, @Nullable byte[] classBytes) {
            String checkClassName = ClassUtils.path2Classname(className);
            if (checkClassName.startsWith(Const.GEN_PKG_SERVICE)) {
                 WMRouterLogger.info(TRANSFORM + "className = %s, checkClassName = %s", className, checkClassName);
                 initClasses.add(className);
            }
            return null;
        }
    }, false);
    baseTransform.setDeleteCallBack(new DeleteCallBack() {
        @Override
        public void delete(String className, byte[] bytes) {
            String checkClassName = ClassUtils.path2Classname(className);
            if (checkClassName.startsWith(Const.GEN_PKG_SERVICE)) {
                deleteClasses.add(className);
            }
        }
    });
    baseTransform.openSimpleScan();
    baseTransform.startTransform();
    File dest = invocation.getOutputProvider().getContentLocation(
            "WMRouter", TransformManager.CONTENT_CLASS,
            ImmutableSet.of(QualifiedContent.Scope.PROJECT), Format.DIRECTORY);
    generateServiceInitClass(dest.getAbsolutePath(), initClasses, deleteClasses);
    WMRouterLogger.info(TRANSFORM + "cost %s ms", System.currentTimeMillis() - ms);
}



private void generateServiceInitClass(String directory, Set<String> classes, Set<String> deleteClass) {
    if (classes.isEmpty()) {
        WMRouterLogger.info(GENERATE_INIT + "skipped, no service found");
        return;
    }
    File dest = new File(directory, INIT_SERVICE_PATH + SdkConstants.DOT_CLASS);
    if (!dest.exists()) {
        try {
            WMRouterLogger.info(GENERATE_INIT + "start...");
            long ms = System.currentTimeMillis();

            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
            ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, writer) {
            };
            // 1、类名
            String className = Const.SERVICE_LOADER_INIT.replace('.', '/');
            cv.visit(50, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);
            
            // 2、init 方法声明 (公开 & 静态)
            MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
                    Const.INIT_METHOD, "()V", null, null);
            // 3、init 方法体
            // 遍历传递的 classes 参数,调用其 init 方法
            mv.visitCode();
            for (String clazz : classes) {
                String input = clazz.replace(".class", "");
                input = input.replace(".", "/");
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, input,
                        "init",
                        "()V",
                        false);
            }
            mv.visitMaxs(0, 0);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitEnd();
            cv.visitEnd();
            // 4、将class字节码写到对应位置
            dest.getParentFile().mkdirs();
            new FileOutputStream(dest).write(writer.toByteArray());

            WMRouterLogger.info(GENERATE_INIT + "cost %s ms", System.currentTimeMillis() - ms);

        } catch (IOException e) {
            WMRouterLogger.fatal(e);
        }
    } else {
        try {
            modifyClass(dest, classes, deleteClass);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码
  • BaseTransform 将所有以 com.sankuai.waimai.router.generated.service 开头的类添加到一个 Set 集合中,之后将其作为参数传递给 generateServiceInitClass 方法;
  • generateServiceInitClass 方法会生成 ServiceLoaderInit 类,并调用 APT 生成的一些列 init 方法进行路由的注册。

3、路由过程

1、Router.startUri ==> RootUriHandler.startUri (实际是DefaultRootUriHandler,在Router.init时传递

DefaultRootUriHandler创建Request, 调用handle) ==> UriHandle.handle

DefaultRootUriHandler 继承 RootUriHandler 继承 ChainedHandler 继承 UriHandler

handle 方法首先判断是否应该处理 shouldHandle (ChainedHandler中,只要 mHandlers列表 不为空就应该处理,mHandlers 在初始化的时候被赋值)

public DefaultRootUriHandler(Context context, @Nullable String defaultScheme, @Nullable String defaultHost) {
    super(context);
    mPageAnnotationHandler = createPageAnnotationHandler();
    mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);

    // 按优先级排序,数字越大越先执行

    // 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发
    addChildHandler(mPageAnnotationHandler, 300);
    // 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的Handler
    addChildHandler(mUriAnnotationHandler, 200);
    // 添加其他用户自定义Handler...
    // 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
    addChildHandler(new StartUriHandler(), -100);
}

public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
    if (shouldHandle(request)) {
        Debugger.i("%s: handle request %s", this, request);
        if (mInterceptor != null && !request.isSkipInterceptors()) {
            mInterceptor.intercept(request, new UriCallback() {
                @Override
                public void onNext() {
                    // 1、先调用拦截器的方法,拦截器调用完成之后再调用 handleInternal
                    handleInternal(request, callback);
                }

                @Override
                public void onComplete(int result) {
                    callback.onComplete(result);
                }
            });
        } else {
            // 没有拦截器直接 handleInternal
            handleInternal(request, callback);
        }
    } else {
        Debugger.i("%s: ignore request %s", this, request);
        callback.onNext();
    }
}
复制代码

因为后面的具体 Handler 基本上都是继承自 UriHandler,因此这一段 handler 的代码相当于时一个模板方法模式,对一个UriRequest的处理基本上都分解成如上的三步:

第一步:调用 shouldHandle 判断是否应该处理;

第二步:判断当前 Handler 是否存在拦截器,如果有拦截器先将 UriRequest 交给拦截器处理;

第三步:将请求交给 handlerInternal 进行处理。

2、到目前为止我们的 Handler 实际的类型还是 DefaultRootUriHandler,其 handlerInternal 在 ChainedHandler 中实现。

@Override
protected void handleInternal(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
    next(mHandlers.iterator(), request, callback);
}

private void next(@NonNull final Iterator<UriHandler> iterator, @NonNull final UriRequest request,
                  @NonNull final UriCallback callback) {
    if (iterator.hasNext()) {
        UriHandler t = iterator.next();
        t.handle(request, new UriCallback() {
            @Override
            public void onNext() {
                next(iterator, request, callback);
            }

            @Override
            public void onComplete(int resultCode) {
                callback.onComplete(resultCode);
            }
        });
    } else {
        callback.onNext();
    }
}
复制代码

基本就是按照 UriHandler 的顺序依次调用其 handler 方法, UriHandler 在 DefaultRootUriHandler 构造时初始化,并被存储在一个优先级队列中。按照优先级依次是:PageAnnotationHandler、UriAnnotationHandler、StartUriHandler。

3、PageAnnotationHandler

public void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {
    mInitHelper.ensureInit();
    super.handle(request, callback);
}

protected void handleInternal(final UriRequest request, final UriCallback callback) {
    // 1、查找能处理的 Handler
    UriHandler h = getChild(request);
    if (h != null) {
        // 2、调用查找到的Handler的handle方法
        h.handle(request, new UriCallback() {
            @Override
            public void onNext() {
                handleByDefault(request, callback);
            }

            @Override
            public void onComplete(int resultCode) {
                callback.onComplete(resultCode);
            }
        });
    } else {
        // 3、调用默认 Handler 的 handle 方法
        handleByDefault(request, callback);
    }
}
// 初始化时注册的路由处理器就是放在 mMap 中
private UriHandler getChild(@NonNull UriRequest request) {
    String path = request.getUri().getPath();
    UriHandler handler;
    if (TextUtils.isEmpty(path)) {
        return mMap.get("/");
    }
    if (!TextUtils.isEmpty(mPathPrefix)) {
        path = path.substring(mPathPrefix.length());
    }
    path = Util.appendSlash(path);
    handler = mMap.get(path);
    if (handler == null) { //modify by zyh 当常规的路由表匹配不上时,从restfulMap中查找匹配的路由信息
        handler = getRestfulChild(request);
    }
    if (handler == null) {
        handler = mMap.get("/");
    }
    return handler;
}
复制代码
  • 先调用 mInitHelper.ensureInit() 判断是否已经初始化,没有初始化则执行初始化操作,初始化其实就是上面的扫描注解处理器生成的样板代码,注册最终路由处理器的过程。

  • 之后在调用基类UriHandler的handle方法进行处理,按照上面的思路,最终会调用到 handleInternal 中,PageAnnotationHandler继承自 PathHandler,PathHandler 中实现了该方法。

  • 在 mMap 中查找到能够处理的 UriHandler 调用其 handler 方法,否则调用默认 handler 的 handle 方法(PageAnnotationHandler)没有默认 Handler

4、FragmentTransactionHandler

FragmentTransactionHandler 就是由 PageAnnotationHandler 注册的一种路由处理器类型,用来路由到一个 Fragment页面。同理我们主要看其 handleInternal 方法。

protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
    if (TextUtils.isEmpty(mClassName)) {
        Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有ClassName");
        callback.onComplete(UriResult.CODE_BAD_REQUEST);
        return;
    }

    StartFragmentAction action = request.getField(StartFragmentAction.class, StartFragmentAction.START_FRAGMENT_ACTION);
    if (action == null) {
        Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有StartFragmentAction");
        callback.onComplete(UriResult.CODE_BAD_REQUEST);
        return;
    }

    if (!request.hasField(FRAGMENT_CLASS_NAME)) {
        //判断一下,便于被替换
        request.putField(FRAGMENT_CLASS_NAME, mClassName);
    }

    // Extra
    Bundle extra = request.getField(Bundle.class, FIELD_INTENT_EXTRA);
    boolean success = action.startFragment(request, extra);
    // 完成
    callback.onComplete(success ? UriResult.CODE_SUCCESS : UriResult.CODE_BAD_REQUEST);
}

public boolean startFragment(@NonNull UriRequest request, @NonNull Bundle bundle) throws ActivityNotFoundException, SecurityException {
    String fragmentClassName = request.getStringField(FragmentTransactionHandler.FRAGMENT_CLASS_NAME);
    try {
        Fragment fragment = Fragment.instantiate(request.getContext(), fragmentClassName, bundle);
        if (fragment == null) {
            return false;
        }

        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        switch (mStartType) {
            case TYPE_ADD:
                transaction.add(mContainerViewId, fragment, mTag);
                break;
            case TYPE_REPLACE:
                transaction.replace(mContainerViewId, fragment, mTag);
                break;
        }
        if (mAllowingStateLoss) {
            transaction.commitAllowingStateLoss();
        } else {
            transaction.commit();
        }
        return true;
    } catch (Exception e) {
        Debugger.e("FragmentTransactionUriRequest",e);
        return false;
    }
}
复制代码
  • 创建了 StartFragmentAction 实例,调用起 startFragment 方法。
  • StartFragmentAction 是一个接口,我们看其中一种实现。
  • 调用 Fragment.instantiate 实例化一个 Fragment 对象,再通过 FragmentTransaction 调用 add 或者 replace 方法将其显示出来。

5、UriAnnotationHandler

如果前一步的 PageAnnotationHandler 没有处理,下一步就由UriAnnotationHandler来处理。其处理逻辑和 PageAnnotationHandler 相似,也是先初始化,然后再调用 UriHandler 的 handle 方法。和 PageAnnotationHandler 不同的是 UriAnnotationHandler 直接继承 UriHandler,而前者直接继承 PathHandler。

public void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {
    mInitHelper.ensureInit();
    super.handle(request, callback);
}

protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
    PathHandler pathHandler = getChild(request);
    if (pathHandler != null) {
        pathHandler.handle(request, callback);
    } else {
        // 没找到的继续分发
        callback.onNext();
    }
}

private final Map<String, PathHandler> mMap = new HashMap<>();
private PathHandler getChild(@NonNull UriRequest request) {
    return mMap.get(request.schemeHost());
}
复制代码
  • 由上面分析可知最终也是进入 handleInternal 方法中,首先从 mMap 中通过 scheme + host 拿到对应的 PathHandler,再调用其 handle 方法 (也是 UriHandler 的 handle)

  • 最终也是调用到 PathHandler 的 handleInternal 方法

  • 在 UriAnnotationHandler 每个 scheme + host 对应一个 PathHandler,而一个 PathHandler 中又可以注册多个路由。

  • 在 PageAnnotationHandler 只有一个 PathHandler (就是其自身,其scheme和host固定)

  • 后面的处理和 PageAnnotationHandler 中分析的类似了:在 PathHandler 查找到具体的路由处理器,调用对应的 handle 方法

6、ActivityClassNameHandler

该 Handler 就是由 UriAnnotationHandler 解析注册的。继承 AbsActivityHandler,最终交给其 handleInternal方法处理。

protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
    // 创建Intent
    Intent intent = createIntent(request);
    if (intent == null || intent.getComponent() == null) {
        Debugger.fatal("AbsActivityHandler.createIntent()应返回的带有ClassName的显式跳转Intent");
        callback.onComplete(UriResult.CODE_ERROR);
        return;
    }
    intent.setData(request.getUri());
    UriSourceTools.setIntentSource(intent, request);
    // 启动Activity
    request.putFieldIfAbsent(ActivityLauncher.FIELD_LIMIT_PACKAGE, limitPackage());
    int resultCode = RouterComponents.startActivity(request, intent);
    // 回调方法
    onActivityStartComplete(request, resultCode);
    // 完成
    callback.onComplete(resultCode);
}
复制代码

4、其他

WMRouter除了页面路由的功能外还提供了ServiceLoader的功能,此外还有@Autowired注解可以自动注入路由传递的参数。

Guess you like

Origin juejin.im/post/7033951637089550373