前言
Android无论App开发还是SDK开发,都绕不开组件化,组件化要解决的最大的问题就是组件之间的通信,即路由框架。国内使用最多的两个路由框架一个是阿里的ARouter,另一个是美团的WMRouter。这两个路由框架功能都很强大,笔者都有使用过。从源码上来看,WMRouter的架构更加清晰,可读性更强。从扩展性来讲,WMRouter更灵活,且具备很强大的无侵入式扩展性。这两个框架都使用了Android开发当中比较前沿的技术,一个是APT技术,一个是字节码插桩技术。WMRouter与Arouter最大的不同是它使用了自定义的ServiceLoader,这个ServiceLoader就是用来加载各个子工程中的服务。今天笔者就挑WMRouter来讲一讲它的实现过程。
本文基于WMRouter源码的1.2.1版本。
笔者原创,转载请注明出处:https://blog.csdn.net/devnn/article/details/136969237
WMRouter的设计与使用文档
关于WMRouter的使用还不了解,可以去githup看一看。先对它有一个整体的认识。
WMRouter设计与使用文档
简单来说,使用分为4步:
1、在Gradle中引入依赖、添加插件
2、初始化
// 创建RootHandler
val rootHandler = DefaultRootUriHandler(this)
// 初始化
Router.init(rootHandler)
3、配置路由
@RouterUri(scheme = "aaa", host = "bbb", path = ["/page1"])
class ActivityTest1 : AppCompatActivity() {
...
}
4、跳转
Router.startUri(context, "aaa://bbb/page1");
WMRouter源码解析
WMRouter初始化
使用WMRouer之前先要初始化,看一下初始化代码
//com.sankuai.waimai.router.Routerpublic class Router {@SuppressLint("StaticFieldLeak")private static RootUriHandler ROOT_HANDLER;/*** 此初始化方法必须在主线程调用。*/public static void init(@NonNull RootUriHandler rootUriHandler) {if (!Debugger.isLogSetting()) {Log.w(Debugger.LOG_TAG, "!!当前未设置Logger,建议通过 Debugger.setLogger()方法设置Logger");Log.w(Debugger.LOG_TAG, "!!并在测试环境通过 Debugger.EnableLog(true)方法开启日志");Log.w(Debugger.LOG_TAG, "!!通过Debugger.setEnableDebug(true)方法在测试环境及时抛出严重类型异常");}if (Looper.myLooper() != Looper.getMainLooper()) {Debugger.fatal("初始化方法init应该在主线程调用");}if (ROOT_HANDLER == null) {ROOT_HANDLER = rootUriHandler;} else {Debugger.fatal("请勿重复初始化UriRouter");}}/*** 此初始化方法的调用不是必须的。* 使用时会按需初始化;但也可以提前调用并初始化,使用时会等待初始化完成。* 本方法线程安全。*/public static void lazyInit() {ServiceLoader.lazyInit();getRootHandler().lazyInit();}public static RootUriHandler getRootHandler() {if (ROOT_HANDLER == null) {throw new RuntimeException("请先调用init初始化UriRouter");}return ROOT_HANDLER;}public static void startUri(UriRequest request) {getRootHandler().startUri(request);}public static void startUri(Context context, String uri) {getRootHandler().startUri(new UriRequest(context, uri));}//省略无关代码
}
可以看到,初始化主要是给ROOT_HANDLER
赋值,调用startUri
方法跳转时,也是委托给了初始化传入的RootUriHandler
。
接下来看看初始化传入的DefaultRootUriHandler
:
package com.sankuai.waimai.router.common;import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;import com.sankuai.waimai.router.annotation.RouterUri;
import com.sankuai.waimai.router.components.DefaultOnCompleteListener;
import com.sankuai.waimai.router.core.RootUriHandler;
import com.sankuai.waimai.router.regex.RegexAnnotationHandler;
import com.sankuai.waimai.router.utils.LazyInitHelper;/*** 默认的RootHandler实现** Created by jzj on 2018/3/23.*/public class DefaultRootUriHandler extends RootUriHandler {private final PageAnnotationHandler mPageAnnotationHandler;private final UriAnnotationHandler mUriAnnotationHandler;private final RegexAnnotationHandler mRegexAnnotationHandler;public DefaultRootUriHandler(Context context) {this(context, null, null);}/*** @param defaultScheme {@link RouterUri} 没有指定scheme时,则使用这里设置的defaultScheme* @param defaultHost {@link RouterUri} 没有指定host时,则使用这里设置的defaultHost*/public DefaultRootUriHandler(Context context,@Nullable String defaultScheme, @Nullable String defaultHost) {super(context);mPageAnnotationHandler = createPageAnnotationHandler();mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);mRegexAnnotationHandler = createRegexAnnotationHandler();// 按优先级排序,数字越大越先执行// 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发addChildHandler(mPageAnnotationHandler, 300);// 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的HandleraddChildHandler(mUriAnnotationHandler, 200);// 处理RouterRegex注解定义的正则匹配addChildHandler(mRegexAnnotationHandler, 100);// 添加其他用户自定义Handler...// 都没有处理,则尝试使用默认的StartUriHandler直接启动UriaddChildHandler(new StartUriHandler(), -100);// 全局OnCompleteListener,用于输出跳转失败提示信息setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);}/*** @see LazyInitHelper#lazyInit()*/public void lazyInit() {mPageAnnotationHandler.lazyInit();mUriAnnotationHandler.lazyInit();mRegexAnnotationHandler.lazyInit();}public PageAnnotationHandler getPageAnnotationHandler() {return mPageAnnotationHandler;}public UriAnnotationHandler getUriAnnotationHandler() {return mUriAnnotationHandler;}public RegexAnnotationHandler getRegexAnnotationHandler() {return mRegexAnnotationHandler;}@NonNullprotected PageAnnotationHandler createPageAnnotationHandler() {return new PageAnnotationHandler();}@NonNullprotected UriAnnotationHandler createUriAnnotationHandler(@Nullable String defaultScheme,@Nullable String defaultHost) {return new UriAnnotationHandler(defaultScheme, defaultHost);}@NonNullprotected RegexAnnotationHandler createRegexAnnotationHandler() {return new RegexAnnotationHandler();}
}
RootUriHandler采用了责任链的设计模式,它是ChainedHandler
的子类:
public class RootUriHandler extends ChainedHandler {
//代码略,主要是一个责任链模式,建议参阅源码。这个不是本文重点。
}
因此DefaultRootUriHandler
只是一个壳,实际业务交给了责任链中的处理器。
回到DefaultRootUriHandler
,可以看到在它的构造方法中添加了责任链的节点(或叫处理器),因此路由的跳转请求交给了这些处理器。
可以看到,一共添加了4个处理器,分别是mPageAnnotationHandler
、mUriAnnotationHandler
、mRegexAnnotationHandler
、new StartUriHandler()
。
mPageAnnotationHandler
是用来兼容老的跳转方式,它跟mUriAnnotationHandler
是区别是,前者的scheme和host是固定的,即wm_router:\\page
,后者的scheme和host支持可配也支持空(不配的情况下默认是空)。
mUriAnnotationHandler
用来处理一般的Uri跳转。
mRegexAnnotationHandler
是用来匹配正则表达式的路由跳转。
StartUriHandler
是用来兜底的处理器。
大多数情况下,使用的是mUriAnnotationHandler
这个Uri处理器。
UriAnnotationHandler处理Uri请求
接下来,我们关注mUriAnnotationHandler
代码,它的类型是UriAnnotationHandler
:
package com.sankuai.waimai.router.common;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;import com.sankuai.waimai.router.annotation.RouterUri;
import com.sankuai.waimai.router.components.RouterComponents;
import com.sankuai.waimai.router.core.UriCallback;
import com.sankuai.waimai.router.core.UriHandler;
import com.sankuai.waimai.router.core.UriInterceptor;
import com.sankuai.waimai.router.core.UriRequest;
import com.sankuai.waimai.router.utils.LazyInitHelper;
import com.sankuai.waimai.router.utils.RouterUtils;import java.util.HashMap;
import java.util.Map;/*** URI跳转,通过注解 {@link RouterUri} 配置,可处理多个Scheme+Host。** 接收到 {@link UriRequest} 时, {@link UriAnnotationHandler} 根据scheme+host产生的key,* 分发给对应的 {@link PathHandler},{@link PathHandler} 再根据path分发给每个子节点。** Created by jzj on 2018/3/23.*/
public class UriAnnotationHandler extends UriHandler {private static boolean sAddNotFoundHandler = true;/*** 设置是否在每个PathHandler上添加一个NotFoundHandler,默认为true。* 如果添加了NotFoundHandler,则只要scheme+host匹配上,所有的URI都会被PathHandler拦截掉,* 即使path不能匹配,也会分发到NotFoundHandler终止分发。*/public static void setAddNotFoundHandler(boolean addNotFoundHandler) {sAddNotFoundHandler = addNotFoundHandler;}/*** key是由scheme+host生成的字符串,value是PathHandler。*/private final Map<String, PathHandler> mMap = new HashMap<>();/*** {@link RouterUri} 没有指定scheme时,则使用这里设置的defaultScheme*/private final String mDefaultScheme;/*** {@link RouterUri} 没有指定host时,则使用这里设置的defaultHost*/private final String mDefaultHost;private final LazyInitHelper mInitHelper = new LazyInitHelper("UriAnnotationHandler") {@Overrideprotected void doInit() {initAnnotationConfig();}};/*** @param defaultScheme {@link RouterUri} 没有指定scheme时,则使用这里设置的defaultScheme* @param defaultHost {@link RouterUri} 没有指定host时,则使用这里设置的defaultHost*/public UriAnnotationHandler(@Nullable String defaultScheme, @Nullable String defaultHost) {mDefaultScheme = RouterUtils.toNonNullString(defaultScheme);mDefaultHost = RouterUtils.toNonNullString(defaultHost);}/*** @see LazyInitHelper#lazyInit()*/public void lazyInit() {mInitHelper.lazyInit();}protected void initAnnotationConfig() {RouterComponents.loadAnnotation(this, IUriAnnotationInit.class);}public PathHandler getPathHandler(String scheme, String host) {return mMap.get(RouterUtils.schemeHost(scheme, host));}/*** 给指定scheme和host的节点设置path前缀*/public void setPathPrefix(String scheme, String host, String prefix) {PathHandler pathHandler = getPathHandler(scheme, host);if (pathHandler != null) {pathHandler.setPathPrefix(prefix);}}/*** 给所有节点设置path前缀*/public void setPathPrefix(String prefix) {for (PathHandler pathHandler : mMap.values()) {pathHandler.setPathPrefix(prefix);}}public void register(String scheme, String host, String path,Object handler, boolean exported, UriInterceptor... interceptors) {// 没配的scheme和host使用默认值if (TextUtils.isEmpty(scheme)) {scheme = mDefaultScheme;}if (TextUtils.isEmpty(host)) {host = mDefaultHost;}String schemeHost = RouterUtils.schemeHost(scheme, host);PathHandler pathHandler = mMap.get(schemeHost);if (pathHandler == null) {pathHandler = createPathHandler();mMap.put(schemeHost, pathHandler);}pathHandler.register(path, handler, exported, interceptors);}@NonNullprotected PathHandler createPathHandler() {PathHandler pathHandler = new PathHandler();if (sAddNotFoundHandler) {pathHandler.setDefaultChildHandler(NotFoundHandler.INSTANCE);}return pathHandler;}/*** 通过scheme+host找对应的PathHandler,找到了才会处理*/private PathHandler getChild(@NonNull UriRequest request) {return mMap.get(request.schemeHost());}@Overridepublic void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {mInitHelper.ensureInit();super.handle(request, callback);}@Overrideprotected boolean shouldHandle(@NonNull UriRequest request) {return getChild(request) != null;}@Overrideprotected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {PathHandler pathHandler = getChild(request);if (pathHandler != null) {pathHandler.handle(request, callback);} else {// 没找到的继续分发callback.onNext();}}@Overridepublic String toString() {return "UriAnnotationHandler";}
}
可以看到它的handle
方法中,首先调用了 mInitHelper.ensureInit()
,用来确保路由组件已经注册:
//com.sankuai.waimai.router.common.UriAnnotationHandler@Overridepublic void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {mInitHelper.ensureInit();super.handle(request, callback);}
如何保证路由组件的注册正是它的技术难点,也是不同于Arouter的地方。
然后在handleInternal
方法中找到跟SchemHost匹配的PathHandler
,然后将请求交给对应的PathHandler。
//com.sankuai.waimai.router.common.UriAnnotationHandler@Overrideprotected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {PathHandler pathHandler = getChild(request);if (pathHandler != null) {pathHandler.handle(request, callback);} else {// 没找到的继续分发callback.onNext();}}
其中,PathHandler
是根据SchemeHost匹配的处理器:
//com.sankuai.waimai.router.common.UriAnnotationHandler/*** 通过scheme+host找对应的PathHandler,找到了才会处理*/private PathHandler getChild(@NonNull UriRequest request) {return mMap.get(request.schemeHost());}
路由注册方法是register
,它并不需要我们手动来调用:
//com.sankuai.waimai.router.common.UriAnnotationHandlerpublic void register(String scheme, String host, String path,Object handler, boolean exported, UriInterceptor... interceptors) {// 没配的scheme和host使用默认值if (TextUtils.isEmpty(scheme)) {scheme = mDefaultScheme;}if (TextUtils.isEmpty(host)) {host = mDefaultHost;}String schemeHost = RouterUtils.schemeHost(scheme, host);PathHandler pathHandler = mMap.get(schemeHost);if (pathHandler == null) {pathHandler = createPathHandler();mMap.put(schemeHost, pathHandler);}pathHandler.register(path, handler, exported, interceptors);}
由于它是支持同一个SchemHost可以有多个path,所以它将同一个SchemeHost下的请求全部交给对应的PathHandler。第一次先创建新的PathHandler,然后放到Map中缓存起来,下次从缓存中取出PathHandler。
实际的路由注册是PathHandler
的resgiter
方法。
接下来就是PathHandler的源码:
//com.sankuai.waimai.router.common.PathHandlerpublic void register(String path, Object target, boolean exported,UriInterceptor... interceptors) {if (!TextUtils.isEmpty(path)) {path = RouterUtils.appendSlash(path);UriHandler parse = UriTargetTools.parse(target, exported, interceptors);UriHandler prev = mMap.put(path, parse);if (prev != null) {Debugger.fatal("[%s] 重复注册path='%s'的UriHandler: %s, %s", this, path, prev, parse);}}}//省略无关代码
UriTargetTools
的parse
方法中生成了path对应的真正处理跳转的UriHandler,比如处理Activity跳转的UriHandler是ActivityClassNameHandler
:
package com.sankuai.waimai.router.components;import android.app.Activity;import com.sankuai.waimai.router.activity.ActivityClassNameHandler;
import com.sankuai.waimai.router.activity.ActivityHandler;
import com.sankuai.waimai.router.common.NotExportedInterceptor;
import com.sankuai.waimai.router.core.UriHandler;
import com.sankuai.waimai.router.core.UriInterceptor;import java.lang.reflect.Modifier;/*** 跳转目标,支持ActivityClass, ActivityClassName, UriHandler。** Created by jzj on 2018/3/26.*/public class UriTargetTools {public static UriHandler parse(Object target, boolean exported,UriInterceptor... interceptors) {UriHandler handler = toHandler(target);if (handler != null) {if (!exported) {handler.addInterceptor(NotExportedInterceptor.INSTANCE);}handler.addInterceptors(interceptors);}return handler;}private static UriHandler toHandler(Object target) {if (target instanceof UriHandler) {return (UriHandler) target;} else if (target instanceof String) {return new ActivityClassNameHandler((String) target);} else if (target instanceof Class && isValidActivityClass((Class) target)) {//noinspection uncheckedreturn new ActivityHandler((Class<? extends Activity>) target);} else {return null;}}private static boolean isValidActivityClass(Class clazz) {return clazz != null && Activity.class.isAssignableFrom(clazz)&& !Modifier.isAbstract(clazz.getModifiers());}
}
关于实际处理跳转的代码请参阅源码,它并不是重点。以上介绍了Router源码的大致流程,其中最重要的还没有讲,就是它是如何自动注册路由的。
UriAnnotationHandler如何注册路由
我们看一下UriAnnotationHandler
的register
方法在哪里调用的。
//com.sankuai.waimai.router.common.UriAnnotationHandlerpublic void register(String scheme, String host, String path,Object handler, boolean exported, UriInterceptor... interceptors) {...}
通过IDE的调用跟踪功能,找到了两处调用:
package com.sankuai.waimai.router.generated;import com.sankuai.waimai.router.common.IUriAnnotationInit;
import com.sankuai.waimai.router.common.UriAnnotationHandler;public class UriAnnotationInit_193ec81a29c7c951924b68ab1dc340aa implements IUriAnnotationInit {public void init(UriAnnotationHandler handler) {handler.register("aaa", "bbb", "/page1", "com.devnn.library1.ActivityTest1", false);handler.register("aaa", "bbb", "/page3", "com.devnn.library1.ActivityTest3", false);}
}
它的位置是对应library1工程的build目录下:
可见,这是APT帮我们生成的代码。
在library1工程中笔者之前声明过两个路由组件:
package com.devnn.library1import androidx.appcompat.app.AppCompatActivity
import com.sankuai.waimai.router.annotation.RouterUri@RouterUri(scheme = "aaa", host = "bbb", path = ["/page1"])
class ActivityTest1 : AppCompatActivity() {
...
}
package com.devnn.library1import androidx.appcompat.app.AppCompatActivity
import com.sankuai.waimai.router.annotation.RouterUri@RouterUri(scheme = "aaa", host = "bbb", path = ["/page3"])
class ActivityTest3 : AppCompatActivity() {...
}
UriAnnotationInit_193ec81a29c7c951924b68ab1dc340aa
正是Router帮我们生成的初始化类。它的init方法帮我们完成了这两个Activity的路由注册。
那么它的init初始化方法是在哪里调用的呢?
通过跟踪是在DefaultAnnotationLoader
中调用的:
package com.sankuai.waimai.router.components;import com.sankuai.waimai.router.Router;
import com.sankuai.waimai.router.core.UriHandler;import java.util.List;/*** 使用ServiceLoader加载注解配置** Created by jzj on 2018/4/28.*/
public class DefaultAnnotationLoader implements AnnotationLoader {public static final AnnotationLoader INSTANCE = new DefaultAnnotationLoader();@Overridepublic <T extends UriHandler> void load(T handler,Class<? extends AnnotationInit<T>> initClass) {List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass);for (AnnotationInit<T> service : services) {service.init(handler);}}
}
DefaultAnnotationLoader的loader方法是在UriAnnotationHandler
的handle方法中调用的:
//com.sankuai.waimai.router.common.UriAnnotationHandler@Overridepublic void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {mInitHelper.ensureInit();super.handle(request, callback);}
handle
方法的第一行代码mInitHelper.ensureInit()
调用了doInit
方法:
//com.sankuai.waimai.router.common.UriAnnotationHandlerprivate final LazyInitHelper mInitHelper = new LazyInitHelper("UriAnnotationHandler") {@Overrideprotected void doInit() {initAnnotationConfig();}};/*** @param defaultScheme {@link RouterUri} 没有指定scheme时,则使用这里设置的defaultScheme* @param defaultHost {@link RouterUri} 没有指定host时,则使用这里设置的defaultHost*/public UriAnnotationHandler(@Nullable String defaultScheme, @Nullable String defaultHost) {mDefaultScheme = RouterUtils.toNonNullString(defaultScheme);mDefaultHost = RouterUtils.toNonNullString(defaultHost);}/*** @see LazyInitHelper#lazyInit()*/public void lazyInit() {mInitHelper.lazyInit();}protected void initAnnotationConfig() {RouterComponents.loadAnnotation(this, IUriAnnotationInit.class);}
接着看 RouterComponents.loadAnnotation
方法:
//com.sankuai.waimai.router.components.RouterComponents@NonNullprivate static AnnotationLoader sAnnotationLoader = DefaultAnnotationLoader.INSTANCE;public static <T extends UriHandler> void loadAnnotation(T handler, Class<? extends AnnotationInit<T>> initClass) {sAnnotationLoader.load(handler, initClass);}
可以看到调用的正是DefaultAnnotationLoader
的load
方法。
DefaultAnnotationLoader
的load
方法(参见上文)调用了Router.getAllServcie(initClass)
方法。
那么Router.getAllServices(initClass)
是如何找到AnnotationInit
的实现类呢?
这又回到了门面类Router
:
//com.sankuai.waimai.router.Routerpublic static <I, T extends I> List<T> getAllServices(Class<I> clazz) {return ServiceLoader.load(clazz).getAll();}
可见它是使用ServiceLoader
来加载服务的。
经过以上分析,路由组件是在ServiceLoader
中注册的。
然而,这并不是jdk中的java.util.ServiceLoader
,而是一个自定义的ServiceLoader,不要被它的名字迷惑了。
WMRouter中的ServiceLoader
package com.sankuai.waimai.router.service;import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;import com.sankuai.waimai.router.annotation.RouterProvider;
import com.sankuai.waimai.router.components.RouterComponents;
import com.sankuai.waimai.router.core.Debugger;
import com.sankuai.waimai.router.interfaces.Const;
import com.sankuai.waimai.router.utils.LazyInitHelper;
import com.sankuai.waimai.router.utils.SingletonPool;import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 通过接口Class获取实现类* <p>* Created by jzj on 2018/3/29.** @param <I> 接口类型*/
public class ServiceLoader<I> {private static final Map<Class, ServiceLoader> SERVICES = new HashMap<>();private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {@Overrideprotected void doInit() {try {// 反射调用Init类,避免引用的类过多,导致main dex capacity exceeded问题Class.forName(Const.SERVICE_LOADER_INIT).getMethod(Const.INIT_METHOD).invoke(null);Debugger.i("[ServiceLoader] init class invoked");} catch (Exception e) {Debugger.fatal(e);}}};/*** @see LazyInitHelper#lazyInit()*/public static void lazyInit() {sInitHelper.lazyInit();}/*** 提供给InitClass使用的初始化接口** @param interfaceClass 接口类* @param implementClass 实现类*/public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {ServiceLoader loader = SERVICES.get(interfaceClass);if (loader == null) {loader = new ServiceLoader(interfaceClass);SERVICES.put(interfaceClass, loader);}loader.putImpl(key, implementClass, singleton);}/*** 根据接口获取 {@link ServiceLoader}*/@SuppressWarnings("unchecked")public static <T> ServiceLoader<T> load(Class<T> interfaceClass) {sInitHelper.ensureInit();if (interfaceClass == null) {Debugger.fatal(new NullPointerException("ServiceLoader.load的class参数不应为空"));return EmptyServiceLoader.INSTANCE;}ServiceLoader service = SERVICES.get(interfaceClass);if (service == null) {synchronized (SERVICES) {service = SERVICES.get(interfaceClass);if (service == null) {service = new ServiceLoader(interfaceClass);SERVICES.put(interfaceClass, service);}}}return service;}/*** key --> class name*/private HashMap<String, ServiceImpl> mMap = new HashMap<>();private final String mInterfaceName;private ServiceLoader(Class interfaceClass) {if (interfaceClass == null) {mInterfaceName = "";} else {mInterfaceName = interfaceClass.getName();}}private void putImpl(String key, Class implementClass, boolean singleton) {if (key != null && implementClass != null) {mMap.put(key, new ServiceImpl(key, implementClass, singleton));}}/*** 创建指定key的实现类实例,使用 {@link RouterProvider} 方法或无参数构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回null*/public <T extends I> T get(String key) {return createInstance(mMap.get(key), null);}/*** 创建指定key的实现类实例,使用Context参数构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回null*/public <T extends I> T get(String key, Context context) {return createInstance(mMap.get(key), new ContextFactory(context));}/*** 创建指定key的实现类实例,使用指定的Factory构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回null*/public <T extends I> T get(String key, IFactory factory) {return createInstance(mMap.get(key), factory);}/*** 创建所有实现类的实例,使用 {@link RouterProvider} 方法或无参数构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回EmptyList,List中的元素不为空*/@NonNullpublic <T extends I> List<T> getAll() {return getAll((IFactory) null);}/*** 创建所有实现类的实例,使用Context参数构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回EmptyList,List中的元素不为空*/@NonNullpublic <T extends I> List<T> getAll(Context context) {return getAll(new ContextFactory(context));}/*** 创建所有实现类的实例,使用指定Factory构造。对于声明了singleton的实现类,不会重复创建实例。** @return 可能返回EmptyList,List中的元素不为空*/@NonNullpublic <T extends I> List<T> getAll(IFactory factory) {Collection<ServiceImpl> services = mMap.values();if (services.isEmpty()) {return Collections.emptyList();}List<T> list = new ArrayList<>(services.size());for (ServiceImpl impl : services) {T instance = createInstance(impl, factory);if (instance != null) {list.add(instance);}}return list;}/*** 获取指定key的实现类。注意,对于声明了singleton的实现类,获取Class后还是可以创建新的实例。** @return 可能返回null*/@SuppressWarnings("unchecked")public <T extends I> Class<T> getClass(String key) {return (Class<T>) mMap.get(key).getImplementationClazz();}/*** 获取所有实现类的Class。注意,对于声明了singleton的实现类,获取Class后还是可以创建新的实例。** @return 可能返回EmptyList,List中的元素不为空*/@SuppressWarnings("unchecked")@NonNullpublic <T extends I> List<Class<T>> getAllClasses() {List<Class<T>> list = new ArrayList<>(mMap.size());for (ServiceImpl impl : mMap.values()) {Class<T> clazz = (Class<T>) impl.getImplementationClazz();if (clazz != null) {list.add(clazz);}}return list;}@SuppressWarnings("unchecked")@Nullableprivate <T extends I> T createInstance(@Nullable ServiceImpl impl, @Nullable IFactory factory) {if (impl == null) {return null;}Class<T> clazz = (Class<T>) impl.getImplementationClazz();if (impl.isSingleton()) {try {return SingletonPool.get(clazz, factory);} catch (Exception e) {Debugger.fatal(e);}} else {try {if (factory == null) {factory = RouterComponents.getDefaultFactory();}T t = factory.create(clazz);Debugger.i("[ServiceLoader] create instance: %s, result = %s", clazz, t);return t;} catch (Exception e) {Debugger.fatal(e);}}return null;}@Overridepublic String toString() {return "ServiceLoader (" + mInterfaceName + ")";}public static class EmptyServiceLoader extends ServiceLoader {public static final ServiceLoader INSTANCE = new EmptyServiceLoader();public EmptyServiceLoader() {super(null);}@NonNull@Overridepublic List<Class> getAllClasses() {return Collections.emptyList();}@NonNull@Overridepublic List getAll() {return Collections.emptyList();}@NonNull@Overridepublic List getAll(IFactory factory) {return Collections.emptyList();}@Overridepublic String toString() {return "EmptyServiceLoader";}}
}
ServiceLoader
的load
方法第一行代码就是初始化ServiceLoader:
//com.sankuai.waimai.router.service.ServiceLoaderpublic static <T> ServiceLoader<T> load(Class<T> interfaceClass) {sInitHelper.ensureInit();...}
//com.sankuai.waimai.router.service.ServiceLoader
private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {@Overrideprotected void doInit() {try {// 反射调用Init类,避免引用的类过多,导致main dex capacity exceeded问题Class.forName(Const.SERVICE_LOADER_INIT).getMethod(Const.INIT_METHOD).invoke(null);Debugger.i("[ServiceLoader] init class invoked");} catch (Exception e) {Debugger.fatal(e);}}};
这正是这个自定义ServiceLoader玄机所在,它通过反射调用
com.sankuai.waimai.router.generated.ServiceLoaderInit
这个固定的类,调用它的init方法来初始化ServiceLoader。WMRouter源码中并没有ServiceLoaderInit
这个类,build目录也没有,因此大胆猜测它是由gradle插件帮我们生成的。
我们将生成的apk反编译,找到了ServiceLoaderInit
这个类,它的字节码内容如下:
.method public static init()V.registers 0invoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_7ba49f44b4136fbacadf8b749184ccb8;->init()Vinvoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_569998d498513846731787b941d88272;->init()Vinvoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_915e2ffdfef22c5fbf4a1c47a37e69a5;->init()Vreturn-void
.end method
可见,它就是将APT生成的各种ServiceInit_xxx类的init方法调用了一遍。
到这里,我们已经知道了路由组件是如何自动注册的。
看流程有点复杂也有点绕,其实最有技术含量的代码就是ServiceLoader
。
WMRouter的ServiceLoader
功能类似于jdk中java.util.ServiceLoader
,用来加载服务的,然后又不同于java.util.ServiceLoader
。
不同点在于WMRouter的ServiceLoader
可以自定义服务的构造方法,而且可以加载特定的服务(带有key的服务),而不是所有服务。
另一个不同点是WMRouter的ServiceLoader
加载服务方式并没有采用SPI技术,而且采用反射机制加载ServiceLoaderInit
类。ServiceLoaderInit
采用字节码插桩技术动态生成,它是用来初始化APT生成的ServiceInit_xxx
类,初始化函数实际上就是往ServiceLoader
中添加服务,这样就完成了服务的注册。在ServiceInit_xxx
类中提供了注册接口,ServiceLoaderInit
类中完成注册接口的调用。ServiceLoaderInit
是在ServiceLoader
的load方法首次调用时通过反射加载。
笔者原创,转载请注明出处:https://blog.csdn.net/devnn/article/details/136969237
@RouterUri注解的作用
在library1工程中使用@RouterUri
给两个Activity分别是ActivityTest1
和ActivityTest3
添加注解,在library1的build/generated/source/kapt/debug
目录下会生成IUriAnnotationInit
服务的实现类:
package com.sankuai.waimai.router.generated;import com.sankuai.waimai.router.common.IUriAnnotationInit;
import com.sankuai.waimai.router.common.UriAnnotationHandler;public class UriAnnotationInit_193ec81a29c7c951924b68ab1dc340aa implements IUriAnnotationInit {public void init(UriAnnotationHandler handler) {handler.register("aaa", "bbb", "/page1", "com.devnn.library1.ActivityTest1", false);handler.register("aaa", "bbb", "/page3", "com.devnn.library1.ActivityTest3", false);}
}
同时在该目录下会生成IUriAnnotationInit
服务的注册类:
package com.sankuai.waimai.router.generated.service;import com.sankuai.waimai.router.common.IUriAnnotationInit;
import com.sankuai.waimai.router.service.ServiceLoader;public class ServiceInit_7ba49f44b4136fbacadf8b749184ccb8 {public static void init() {ServiceLoader.put(IUriAnnotationInit.class, "com.sankuai.waimai.router.generated.UriAnnotationInit_193ec81a29c7c951924b68ab1dc340aa", com.sankuai.waimai.router.generated.UriAnnotationInit_193ec81a29c7c951924b68ab1dc340aa.class, false);}
}
@RouterService注解的作用
当使用@RouterService注解会生成Service的注册类。
示例:在library1工程中声明一个@RouterService
组件。
package com.devnn.library1import android.util.Log
import com.devnn.baselibrary.IWMService
import com.sankuai.waimai.router.annotation.RouterService@RouterService(interfaces=[IWMService::class], key =["WMLib1Service"])
class WMLib1Service:IWMService{override fun init() {Log.d("IWMService", "WMLib1Service_init")}}
在libary1工程的build/generated/source/kapt/debug
目录下生成的类:
package com.sankuai.waimai.router.generated.service;import com.devnn.baselibrary.IWMService;
import com.devnn.library1.WMLib1Service;
import com.sankuai.waimai.router.service.ServiceLoader;public class ServiceInit_569998d498513846731787b941d88272 {public static void init() {ServiceLoader.put(IWMService.class, "WMLib1Service", WMLib1Service.class, false);}
}
示例:在library2工程中声明一个@RouterService组件。
package com.devnn.library2import android.util.Log
import com.devnn.baselibrary.IWMService
import com.sankuai.waimai.router.annotation.RouterService@RouterService(interfaces=[IWMService::class], key =["WMLib2Service"])
class WMLib2Service:IWMService {override fun init() {Log.d("IWMService", "WMLib2Service_init")}
}
在libary2工程的build/generated/source/kapt/debug
目录下生成的类:
package com.sankuai.waimai.router.generated.service;import com.devnn.baselibrary.IWMService;
import com.devnn.library2.WMLib2Service;
import com.sankuai.waimai.router.service.ServiceLoader;public class ServiceInit_915e2ffdfef22c5fbf4a1c47a37e69a5 {public static void init() {ServiceLoader.put(IWMService.class, "WMLib2Service", WMLib2Service.class, false);}
}
Gradle插件自动生成的ServiceLoaderInit
类的字节码上文已经贴出,笔者将它转成了Java代码:
package com.sankuai.waimai.router.generated.ServiceLoaderInit;public class ServiceLoaderInit {public ServiceLoaderInit() {com.sankuai.waimai.router.generated.service.ServiceInit_7ba49f44b4136fbacadf8b749184ccb8.init();com.sankuai.waimai.router.generated.service.ServiceInit_569998d498513846731787b941d88272.init();com.sankuai.waimai.router.generated.service.ServiceInit_915e2ffdfef22c5fbf4a1c47a37e69a5.init();}
}
关于如何生成ServiceLaoderInit
的代码,有兴趣可以参阅源码:
https://github.com/meituan/WMRouter/blob/master/WmPlugin/plugin/src/main/java/com/sankuai/waimai/router/plugin/WMRouterTransform.java
总结
经过上文分析,WMRouter的Uri跳转是由UriAnnotationHandler
处理器完成的。在UriAnnotationHandler的handle
方法中首先初始化由@RouterUri
注解生成的服务。初始化功能即完成路由组件的注册。这个过程分两步,第一步是先找到服务,这个是由ServiceLoader完成的。第二步是注册,这个是服务的init(...)
方法完成的。整体流程如下:
笔者原创,转载请注明出处:https://blog.csdn.net/devnn/article/details/136969237