【Android】美团组件化路由框架WMRouter源码解析

前言

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.Router

  public 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跳转,如果注解没定义,继续分发到后面的Handler
        addChildHandler(mUriAnnotationHandler, 200);
        // 处理RouterRegex注解定义的正则匹配
        addChildHandler(mRegexAnnotationHandler, 100);
        // 添加其他用户自定义Handler...

        // 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
        addChildHandler(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;
    }

    @NonNull
    protected PageAnnotationHandler createPageAnnotationHandler() {
        return new PageAnnotationHandler();
    }

    @NonNull
    protected UriAnnotationHandler createUriAnnotationHandler(@Nullable String defaultScheme,
                                                              @Nullable String defaultHost) {
        return new UriAnnotationHandler(defaultScheme, defaultHost);
    }

    @NonNull
    protected RegexAnnotationHandler createRegexAnnotationHandler() {
        return new RegexAnnotationHandler();
    }
}

RootUriHandler采用了责任链的设计模式,它是ChainedHandler的子类:

public class RootUriHandler extends ChainedHandler {
//代码略,主要是一个责任链模式,建议参阅源码。这个不是本文重点。
}

因此DefaultRootUriHandler只是一个壳,实际业务交给了责任链中的处理器。

回到DefaultRootUriHandler,可以看到在它的构造方法中添加了责任链的节点(或叫处理器),因此路由的跳转请求交给了这些处理器。

可以看到,一共添加了4个处理器,分别是mPageAnnotationHandlermUriAnnotationHandlermRegexAnnotationHandlernew 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") {
        @Override
        protected 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);
    }

    @NonNull
    protected 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());
    }

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

    @Override
    protected boolean shouldHandle(@NonNull UriRequest request) {
        return getChild(request) != null;
    }

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

    @Override
    public String toString() {
        return "UriAnnotationHandler";
    }
}

可以看到它的handle方法中,首先调用了 mInitHelper.ensureInit(),用来确保路由组件已经注册:

//com.sankuai.waimai.router.common.UriAnnotationHandler
  @Override
    public 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
  @Override
    protected 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.UriAnnotationHandler
  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);
    }

由于它是支持同一个SchemHost可以有多个path,所以它将同一个SchemeHost下的请求全部交给对应的PathHandler。第一次先创建新的PathHandler,然后放到Map中缓存起来,下次从缓存中取出PathHandler。

实际的路由注册是PathHandlerresgiter方法。

接下来就是PathHandler的源码:

//com.sankuai.waimai.router.common.PathHandler
 public 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);
            }
        }
    }
    //省略无关代码

UriTargetToolsparse方法中生成了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 unchecked
            return 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如何注册路由

我们看一下UriAnnotationHandlerregister方法在哪里调用的。

//com.sankuai.waimai.router.common.UriAnnotationHandler
  public 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.library1

import androidx.appcompat.app.AppCompatActivity
import com.sankuai.waimai.router.annotation.RouterUri

@RouterUri(scheme = "aaa", host = "bbb", path = ["/page1"])
class ActivityTest1 : AppCompatActivity() {
...
}
package com.devnn.library1

import 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();

    @Override
    public <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
 @Override
    public void handle(@NonNull UriRequest request, @NonNull UriCallback callback) {
        mInitHelper.ensureInit();
        super.handle(request, callback);
    }

handle方法的第一行代码mInitHelper.ensureInit()调用了doInit方法:

//com.sankuai.waimai.router.common.UriAnnotationHandler
 private final LazyInitHelper mInitHelper = new LazyInitHelper("UriAnnotationHandler") {
      @Override
      protected 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
  @NonNull
 private static AnnotationLoader sAnnotationLoader = DefaultAnnotationLoader.INSTANCE;
  
 public static <T extends UriHandler> void loadAnnotation(T handler, Class<? extends AnnotationInit<T>> initClass) {
      sAnnotationLoader.load(handler, initClass);
  }

可以看到调用的正是DefaultAnnotationLoaderload方法。

DefaultAnnotationLoaderload方法(参见上文)调用了Router.getAllServcie(initClass)方法。

那么Router.getAllServices(initClass)是如何找到AnnotationInit的实现类呢?
这又回到了门面类Router:

//com.sankuai.waimai.router.Router
  public 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") {
        @Override
        protected 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中的元素不为空
     */
    @NonNull
    public <T extends I> List<T> getAll() {
        return getAll((IFactory) null);
    }

    /**
     * 创建所有实现类的实例,使用Context参数构造。对于声明了singleton的实现类,不会重复创建实例。
     *
     * @return 可能返回EmptyList,List中的元素不为空
     */
    @NonNull
    public <T extends I> List<T> getAll(Context context) {
        return getAll(new ContextFactory(context));
    }

    /**
     * 创建所有实现类的实例,使用指定Factory构造。对于声明了singleton的实现类,不会重复创建实例。
     *
     * @return 可能返回EmptyList,List中的元素不为空
     */
    @NonNull
    public <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")
    @NonNull
    public <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")
    @Nullable
    private <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;
    }

    @Override
    public String toString() {
        return "ServiceLoader (" + mInterfaceName + ")";
    }

    public static class EmptyServiceLoader extends ServiceLoader {

        public static final ServiceLoader INSTANCE = new EmptyServiceLoader();

        public EmptyServiceLoader() {
            super(null);
        }

        @NonNull
        @Override
        public List<Class> getAllClasses() {
            return Collections.emptyList();
        }

        @NonNull
        @Override
        public List getAll() {
            return Collections.emptyList();
        }

        @NonNull
        @Override
        public List getAll(IFactory factory) {
            return Collections.emptyList();
        }

        @Override
        public String toString() {
            return "EmptyServiceLoader";
        }
    }
}

ServiceLoaderload方法第一行代码就是初始化ServiceLoader:

//com.sankuai.waimai.router.service.ServiceLoader
   public static <T> ServiceLoader<T> load(Class<T> interfaceClass) {
        sInitHelper.ensureInit();
        ...
     }
//com.sankuai.waimai.router.service.ServiceLoader
private static final LazyInitHelper sInitHelper = new LazyInitHelper("ServiceLoader") {
       @Override
       protected 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 0

  invoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_7ba49f44b4136fbacadf8b749184ccb8;->init()V

  invoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_569998d498513846731787b941d88272;->init()V

  invoke-static {}, Lcom/sankuai/waimai/router/generated/service/ServiceInit_915e2ffdfef22c5fbf4a1c47a37e69a5;->init()V

  return-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分别是ActivityTest1ActivityTest3添加注解,在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.library1

import 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.library2

import 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(...)方法完成的。整体流程如下:

Created with Raphaël 2.3.0 Router.startUri(...) UriAnnotationHandler.handle(...) initAnnotationConfig() RouterComponents.loadAnnotation(this, IUriAnnotationInit.class); DefaultAnnotationLoader.load(this, IUriAnnotationInit.class); Router.getAllServices(initClass); 通过`ServiceLoader`加载initClass即IUriAnnotationInit的所有实现; 遍历这些实现类,调用init方法(将UriAnnotationHandler传给它) init方法中将路由组件注册到UriAnnotationHandler中 UriAnnotationHandler的getChild方法找到SchemeHost对应的PathHandler 完成跳转

笔者原创,转载请注明出处:https://blog.csdn.net/devnn/article/details/136969237

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/486262.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能运维的发展演进

Gartner在2018年提出AIOps&#xff08;Artificial Intelligence for IT Operations&#xff09;&#xff0c;即人工智能在IT运维领域的应用。智能运维在技术方案、平台、场景都更加聚焦&#xff0c;恰逢AI技术飞速发展。用户可以实时监控分析大量的运维数据&#xff0c;预防和防…

python + tensorflow 开局托儿所自动点击脚本

python开局托儿所自动点击脚本 屏幕截图图片数字识别消除算法自动点击 屏幕截图 python 屏幕截图可以使用pyautogui或者PIL。我使用的是PIL中的ImageGrab(要授权)。 image ImageGrab.grab(bbox(0, 0, tool.static_window_width, tool.static_window_height)) image np.arra…

ModbusRTU/TCP/profinet网关在西门子博图软件中无法连接PLC的解决方法

ModbusRTU/TCP/profinet网关在西门子博图软件中无法连接PLC的解决方法 在工业生产现场&#xff0c;ModbusRTU/TCP/profinet网关在与西门子PLC连接时&#xff0c;必须要使用西门子的博图软件来进行配置&#xff0c;博图v17是一个集成软件平台&#xff0c;专业版支持300、400、12…

海外基金牌照的优势及注意事项-华媒舍

一、了解海外基金牌照 在投资领域&#xff0c;海外基金牌照是指投资者可以通过获得海外金融监管机构颁发的许可证&#xff0c;参与海外基金投资。拥有海外基金牌照的投资者可以享受更广泛的投资机会&#xff0c;包括跨境投资、全球资产配置等。 二、海外基金牌照的优势 多元化…

Unity 学习日记 8.2D物理引擎

1.2D刚体的属性和方法 2.碰撞器

还在购买蜘蛛池做SEO?有用吗?

蜘蛛池是什么&#xff1f;租用蜘蛛池对SEO优化到底有没有用&#xff1f;网上很多说法&#xff0c;且各执一词&#xff0c;那些出租蜘蛛池的写的软文不算。站长帮一直本着负责任的态度&#xff0c;从客观的角度&#xff0c;来为大家一一解惑。 本文 虚良SEO 原创&#xff0c;转载…

如何查询网贷大数据信用报告?哪个查询平台更好?

在互联网金融迅速发展的当下&#xff0c;网贷大数据查询平台已成为许多人在申请贷款前的重要工具。然而&#xff0c;随着这些平台的广泛使用&#xff0c;安全问题日益凸显&#xff0c;许多用户反映自己的个人信息在查询过程中被泄露。为了应对这一挑战&#xff0c;本文将探讨如…

fiddler配合夜神模拟器对APP进行抓包

fiddler 配置 设置https Tools – -> Options —> HTTPS 在这里插入图片描述 下载证书&#xff0c;并安装 修改模拟器网络连接 cmd 查看本机本地IP点击模拟器wifi, 长按修改为手动配置&#xff1a; IP 8888使用浏览器&#xff0c;访问IP 8888 下载证书 。点击Fiddler…

RabbitMQ详细讲解

目录 4.0 AMQP协议的回顾 4.1 RabbitMQ支持的消息模型 4.2 引入依赖 4.3 第一种模型(直连) 1. 开发生产者 2. 开发消费者 3. 参数的说明 4.4 第二种模型(work quene) 1. 开发生产者 2.开发消费者-1 3.开发消费者-2 4.测试结果 5.消息自动确认机制 4.5 第三种模型(…

【力扣白嫖日记】1069.产品销售分析II

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1069.产品销售分析II 表&#xff1a;Sales 列名类型sale_idintproduct_idintyearintquantityintpriceint s…

【Redis】Redisson实现分布式锁

Redisson是一个在Redis的基础上实现的Java驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;。它不仅提供了一系列的分布式的Java常用对象&#xff0c;还提供了许多分布式服务&#xff0c;其中就包含了各种分布式锁的实现。 官网地址 GitHub地址 Redisson入门 1.引…

2024年中国数字经济行业市场前景预测研究报告

随着数字化技术的飞速发展&#xff0c;数字经济已经成为全球经济的重要组成部分。它不仅改变了传统产业的商业模式和运营方式&#xff0c;也催生了许多新兴产业和业态。数字经济的核心在于数据&#xff0c;通过数据的采集、分析和应用&#xff0c;可以实现精准营销、个性化服务…

RabbitMQ 的高阶应用及可靠性保证

目录 一、RabbitMQ 高阶应用 1.1 消息何去何从 1.2 过期时间 1.3 死信队列 1.4 延迟队列 1.5 优先级队列 1.6 消费质量保证&#xff08;QOS&#xff09; 二、持久化 三、生产者确认 四、消息可靠性和重复消费 4.1 消息可靠性 4.2 重复消费问题 上篇文章介绍了 Rabb…

皓学IT:JavaWEB_Cookie

一、Cookie 1.1.Cookie概述 Cookie翻译成中文是小甜点&#xff0c;小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie就是一个键和一个值构成的&#xff0c;随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来&#xff0c;当…

【快刊合集】计算机类SCI,IEEE出版社,中科院2/1区TOP,分数逐年攀升!!

本期推荐 【SciencePub学术】本期&#xff0c;小编给大家推荐的是1本计算机类的甄选好刊&#xff0c;该期刊隶属于IEEE出版社旗下&#xff0c;最新的影响因子已达到7&#xff0c;是1本业内认可度非常高的期刊。 01 期刊基本信息 【期刊简介】IF&#xff1a;7.5-8.0&#xff0…

Git进阶命令-reset

一、reset命令使用场景 有时候我们提交了一些错误的或者不完善的代码&#xff0c;需要回退到之前的某个稳定的版本,面对这种情况有两种解决方法: 解决方法1&#xff1a;修改错误内容&#xff0c;再次commit一次 解决方法2&#xff1a;使用git reset 命令撤销这一次错误的com…

网络类型及数据链路层协议

目录 一、网络的分类 二、数据链路层协议 1、MA网络以太网协议 2、P2P网络 3、HDLC ---高级数据链路控制协议 HDLC地址借用 三、PPP协议 1、PPP协议的优点 2、PPP数据帧封装结构 3、PPP会话的搭建 4、LCP建立——链路建立阶段 4.1协商阶段 4.2认证阶段 4.3 PAP---密…

Java基础【上】韩顺平(反射、类加载、final接口、抽象类、内部类)

涵盖知识点&#xff1a;反射、类加载、单例模式、final、抽象类、接口、内部类&#xff08;局部内部类、匿名内部类、成员内部类、静态内部类&#xff09; P711 反射机制原理 创建如下目录结构&#xff0c;在模块下创建src文件夹&#xff0c;文件夹要设置为Sources文件夹&…

农夫山泉财报公布在即,消费升级的瓶装水市场或将重新洗牌

农夫山泉财报公布在即&#xff0c;消费升级的瓶装水市场或将重新洗牌 新年伊始&#xff0c;2024年对于中国瓶装水行业注定是一个地动山摇的一年&#xff0c;随着农夫山泉董事长钟睒睒上次被媒体集体关注&#xff0c;农夫山泉遭遇上市以来的最大舆论风波。 3月26日农夫山泉的财…

2024 年 8 个最佳 PDF 转 JPG 转换器[免费和付费]

虽然 PDF&#xff08;便携式文档文件&#xff09;是一种流行的文档共享格式&#xff0c;但有时您可能希望将 PDF 文件转换为JPG&#xff0c;然后在网页或社交媒体上共享它们。 在本文中&#xff0c;我们将讨论适用于 Windows 10 和 11 的出色 PDF 到 JPG 转换器的所有特性。 …