跨模块依赖注入


模块化能力支持项:

1. 依赖注入

用于跨模块通信使用,核心设计思想是参考了SOA(面向服务架构) 的设计方式。 具体到 Android 侧就是 AIDL 类似的实现:
例如当前有两个模块:A订单模块、B登录模块,下单需要获取用户信息。
这个业务场景就是,A需要使用获取用户信息的服务,B需要向外提供一个获取用户信息的服务。

1.1 声明接口服务

首先声明一个接口,放入公共依赖层

// 假设当前有一个用户信息获取服务
public interface IUserService {
    String getUserInfo();
}

1.2 服务使用方

也就是上面例子的 A订单模块,他需要使用获取用户信息的服务 A无需关心,IUserService这个接口服务是谁提供的,他只需要知道自己需要使用这样的一个服务就行了。
如果没有提供服务的提供方,TheRouter.get()可能返回null

TheRouter.get(IUserService::class.java)?.getUserInfo()

1.3 服务提供方

服务提供方需要声明一个提供服务的方法,用@ServiceProvider注解标记。

/**
 * 方法名不限定,任意名字都行
 * 返回值必须是服务接口名,如果是实现了服务的子类,需要加上returnType限定(例如下面代码)
 * 方法必须加上 public static 修饰,否则编译期就会报错
 */
@ServiceProvider
public static IUserService test() {
    return new IUserService() {
        @Override
        public String getUserInfo() {
            return "返回用户信息";
        }
    };
}

// 也可以直接返回对象,然后标注这个方法的服名是什么
@ServiceProvider(returnType = IUserService.class)
public static UserServiceImpl test() {
    xxx
}

:每个服务只能有一个服务提供方,类似ARouter那种多个接口实现类,我们认为是不符合面向服务设计规范的。如果你一定要有多个服务,并且由使用方决定在什么时间使用哪个服务,建议选择如下改造方案:

2. 自定义服务拦截器

使用场景:单模块调试时,可能会有需要 mock 其他模块提供的服务,TheRouter 允许自定义其他模块的实现。

Interceptor interceptor = new Interceptor() {
    @Override
    public <T> T interception(Class<T> clazz, Object... params) {
        if (clazz == IUserService.class) {
            return new IUserService();
        }
        return null;
    }
};
TheRouter.getRouterInject().addInterceptor(interceptor);

3. 服务缓存

服务提供方运行对提供的服务做配置,对于无状态的服务,尽可能使用缓存方式减少对象创建次数,而有状态的服务,则每次创建新对象保证多次调用有不会互相污染状态(例如订单状态管理、商品销售状态等服务)。

声明服务缓存只需要在接口协议上新增额外注解即可,例如下面示例代码:

如果两个注解同时被添加,则只有Singleton会生效。
@Singleton@NewInstance需要加在接口协议上。


// 注:如果都不加,默认是LRU+软引用缓存
// 如果两个注解同时被添加,则只有Singleton会生效。
@Singleton // 对外部调用方而言相当于这个对象声明成了单例
@NewInstance  // 每次都会返回新对象
public interface IUserService {
    public String getUserInfo();
}


@ServiceProvider
public static IUserService test() {
    return new IUserService() {
        @Override
        public String getUserInfo() {
            return "返回用户信息";
        }
    };
}

4. 在类上注解ServiceProvider

1.1.2-rc7版本开始,ServiceProvider支持给类设置注解。

// 服务提供方
@ServiceProvider
public class TestClassAnnotation implements ITestClassAnnotation {
}

//服务使用方
TheRouter.get(ITestClassAnnotation.class)

如果一个接口都没有实现,默认的返回类型为当前类本身。
如果 类有多个接口需要实现,则必须显示声明returnType,例如:

// 服务提供方
@ServiceProvider(returnType = ITestClassAnnotation.class)
public class TestClassAnnotation implements ITestClassAnnotation, ITest0 {
}

//服务使用方
TheRouter.get(ITestClassAnnotation.class)

5. 多参数服务提供者

5.1 静态方法作为服务提供者

// 服务提供方
@ServiceProvider
public static IUserService test(String str) {
    return new IUserService() {
        @Override
        public String getUserInfo() {
            return str;
        }
    };
}

//服务使用方
TheRouter.get(IUserService.class, "user Info");

5.2 类对象作为服务提供者

// 服务提供方
@ServiceProvider(returnType = ITestClassAnnotation.class, params={String.class})
public class TestClassAnnotation implements ITestClassAnnotation, ITest0 {
	public TestClassAnnotation(String str){}
}

//服务使用方
TheRouter.get(ITestClassAnnotation.class, "hello")

6. 设置继承的服务实现

如果你的类有父类实现了接口协议,而你又不想显示声明returnType,例如下面这种场景。我们也提供了一个编译选项可供使用,在gradle.properties文件中加入 USE_EXTENSION=true

:请 谨慎 开启 USE_EXTENSION。当开启后,所有协议判断会使用isAssignableFrom去判断,而不再是equals,对运行时性能会有较大影响。

public class SuperTestClassAnnotation implements ITestClassAnnotation, ITest0 {
}

// 服务提供方
@ServiceProvider
public class TestClassAnnotation extends SuperTestClassAnnotation{
}

// 当在`gradle.properties`文件中加入 `USE_EXTENSION=true`后,
// 表示允许接口协议拥有继承关系,所以可以使用如下两个协议获取并使用服务
TheRouter.get(ITestClassAnnotation.class)
// 或
TheRouter.get(ITest0.class)

相关推荐:

TheRouter 使用 KSP 处理注解TheRouter 使用 KSP 处理注解

TheRouter 在`1.1.4-rc2`开始,已经将`KSP`能力合并至主分支,不再是单独的功能分支了,下面介绍一下如何使用 `KSP`。 ##...

2 mins
业务节点订阅(自动初始化)能力介绍业务节点订阅(自动初始化)能力介绍

模块化开发时,可能每个模块都有自己需要初始化的一些代码。以前的做法是把这些代码都在`Application`里声明,但是这样可能随着业务变动每次都需要修改`Application`所在模块。`TheRouter`的单模块自动初始化能力就是为了解决这样的情况,可以只在当前模块声明初始化方法后,将会在业务场景时自动被调用。

8 mins
动态化能力支持:ActionManager动态化能力支持:ActionManager

Action 本质是一个全局的系统回调,主要用于预埋的一系列操作,例如:弹窗、上传日志、清理缓存。 与 Android 系统自带的广播通知类似,你可以在任何地方声明动作与处理方式。并且所有 Action 都是可以被跟踪的,只要你愿意,可以在日志中将所有的动作调用栈输出,以方便调试使用。

6 mins