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

news2024/11/15 16:51:32

前言

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript 中内存泄漏的几种情况(非常详细)

文章目录 一、是什么二、垃圾回收机制标记清除引用计数小结 三、常见内存泄露情况参考文献 一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而…

如何使用 ArcGIS Pro 制作三维建筑

三维地图已经逐渐成为未来地图的趋势&#xff0c;对于大范围应用&#xff0c;只需要普通的建筑体块就行&#xff0c;如果有高程数据&#xff0c;还可以结合地形进行显示&#xff0c;这里为大家介绍一下 ArcGIS Pro 制作三维建筑的方法&#xff0c;希望能对你有所帮助。 数据来…

容器镜像加速指南:探索 Kubernetes 缓存最佳实践

介绍 将容器化应用程序部署到 Kubernetes 集群时&#xff0c;由于从 registry 中提取必要的容器镜像需要时间&#xff0c;因此可能会出现延迟。在应用程序需要横向扩展或处理高速实时数据的情况下&#xff0c;这种延迟尤其容易造成问题。幸运的是&#xff0c;有几种工具和策略…

文件操作示例

1.C文件操作 1.1文件的使用方式 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> #include<errno.h>int main() {FILE* pf fopen("test.txt", "w");if (pf NULL){printf("%s\…

2015年认证杯SPSSPRO杯数学建模C题(第二阶段)荒漠区动植物关系的研究全过程文档及程序

2015年认证杯SPSSPRO杯数学建模 C题 荒漠区动植物关系的研究 原题再现&#xff1a; 环境与发展是当今世界所普遍关注的重大问题, 随着全球与区域经济的迅猛发展, 人类也正以前所未有的规模和强度影响着环境、改变着环境, 使全球的生命支持系统受到了严重创伤, 出现了全球变暖…

代码随想录算法训练营第二十一天(二叉树VII)| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先(JAVA)

文章目录 530. 二叉搜索树的最小绝对差解题思路源码 501. 二叉搜索树中的众数解题思路源码 236. 二叉树的最近公共祖先解题思路源码 530. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&a…

High 级别反射型 XSS 攻击演示(附链接)

环境准备 如何搭建 DVWA 靶场保姆级教程&#xff08;附链接&#xff09;https://eclecticism.blog.csdn.net/article/details/135834194?spm1001.2014.3001.5502 测试 打开靶场找到该漏洞页面 先右键检查输入框属性 还是和之前一样的&#xff0c;所以直接输入 HTML 标签提交…

【Java八股面试系列】中间件-Redis

目录 Redis 什么是Redis Redis解决了什么问题 Redis的实现原理 数据结构 String 常用命令 应用场景 List(列表) 常用命令 应用场景 Hash(哈希) 常用命令 应用场景 set(集合) 常见命令​编辑 应用场景 Sorted Set(有序集合) 常见命令​编辑 应用场景 数据持…

GitHub加速访问最简单的方法

Github是全球最大的代码开源平台&#xff0c;对于编程的小伙伴来说&#xff0c;这是一个巨大的宝库&#xff0c;也是编程学习的圣地。很对小伙伴在使用GitHub时会经常出现无法访问Github的情况。 一、解决方法——>修改hosts文件 通过 IP查询工具来获取当前Github网站的真实…

计算机网络:现代通信的基石

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

AcWing 4609:火柴棍数字 ← 贪心算法

【题目来源】 https://www.acwing.com/problem/content/4612/【题目描述】 给定 n 个火柴棍&#xff0c;你可以用它们摆出数字 0∼9。 摆出每个数字所需要的具体火柴棍数量如下图所示&#xff1a; 请你用这些火柴棍摆成若干个数字&#xff0c;并把这些数字排成一排组成一个整数…

Redis中的事件

事件 概述 Redis服务器是一个事件驱动程序:服务器需要处理以下两类事件: 1.文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接&#xff0c;而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或者其他服务器)的通信会产生相应的文件…

机器学习作业二之KNN算法

KNN&#xff08;K- Nearest Neighbor&#xff09;法即K最邻近法&#xff0c;最初由 Cover和Hart于1968年提出&#xff0c;是一个理论上比较成熟的方法&#xff0c;也是最简单的机器学习算法之一。该方法的思路非常简单直观&#xff1a;如果一个样本在特征空间中的K个最相似&…

Ubuntu deb文件 安装 MySQL

更新系统软件依赖 sudo apt update && sudo apt upgrade下载安装包 输入命令查看Ubuntu系统版本 lsb_release -a2. 网站下载对应版本的安装包 下载地址. 解压安装 mkdir /home/mysqlcd /home/mysqltar -xvf mysql-server_8.0.36-1ubuntu20.04_amd64.deb-bundle.tar# …

Pandas操作MultiIndex合并行列的Excel,写入读取以及写入多余行及Index列处理,插入行,修改某个单元格的值

Pandas操作MultiIndex合并行列的excel&#xff0c;写入读取以及写入多余行及Index列处理 1. 效果图及问题2. 源码参考 今天是谁写Pandas的 复合索引MultiIndex&#xff0c;写的糊糊涂涂&#xff0c;晕晕乎乎。 是我呀… 记录下&#xff0c;现在终于灵台清明了。 明天在记录下直…

02-K近邻算法

机器学习其实有一个很朴实的想法: 预测 x x x的值&#xff0c; 那就在训练集 X X X中找到与 x x x相似的样本&#xff0c; 再把与x相似的这些样本的值加权作为预测值 那么我们如何度量样本之间的相似性&#xff1f;又该如何加权呢&#xff1f; 在k近邻中&#xff0c; 我们一般采…

【CXL协议-事务层之CXL.cache (3)】

3.2 CXL.cache 3.2.1 概述 CXL.cache 协议将设备和主机之间的交互定义为许多请求&#xff0c;每个请求至少有一个关联的响应消息&#xff0c;有时还有数据传输。 该接口由每个方向的三个通道组成&#xff1a; 请求、响应和数据。 这些通道根据其方向命名&#xff0c;D2H&…

基于FPGA实现的自适应三速以太网

一、三速以太网 千兆以太网PHY芯片是适配百兆和十兆的&#xff0c;十兆就不管了&#xff0c;我们的设计只适应千兆和百兆。 根据上图&#xff0c;我们是可以获取当前主机网口的速率信息的。 always(posedge w_rxc_bufr) beginif(w_rec_valid d0) beginro_speed < w_rec_…

【r-tree算法】一篇文章讲透~

目录 一、引言 二、R-tree算法的基本原理 1 数据结构 2 插入操作 3 删除操作 4 查询操作 5 代码事例 三、R-tree算法的性能分析 1 时间复杂度 2 空间复杂度 3 影响因素 四、R-tree算法的变体和改进 1 R*-tree算法 2 X-tree算法 3 QR-tree算法 五、R-tree算法的…

【物联网】Qinghub Kafka 数据采集

基础信息 组件名称 &#xff1a; kafka-connector 组件版本&#xff1a; 1.0.0 组件类型&#xff1a; 系统默认 状 态&#xff1a; 正式发布 组件描述&#xff1a;通用kafka连接网关&#xff0c;消费来自kafka的数据&#xff0c;并转发给下一个节点做相关的数据解析。 配置文…