Skip to content

跨模块服务调用

兮尘 edited this page Jan 12, 2021 · 6 revisions

服务发现其实就是 接口+实现类 的方式,接口对外公开,实现类隐藏,实现类一般在某一个业务模块中。

相比 CC 组件化, CC 虽然更好的隐藏了所有的实现细节,但是调用不够直观和难以寻找。

所以公开暴露接口更有优势,通过接口返回此接口的对象(实际上是一个实现类),然后调用,这种方式就是隐藏了实现类,但是保证了调用都是很明显和直观的。

目录

定义服务接口

当你想提供业务组件1的功能出去的时候,你在基础库(BaseModule)的service包下面新建一个接口文件,这个接口会被所有的业务模块引用到

image.png

public interface Component1Service {
    Fragment getFragment();
    void xxx();
    int count();
    ......
}

实现服务接口

非单例服务

@ServiceAnno(value = {Component1Service.class},singleTon = false)
public class Component1ServiceImpl implements Component1Service {

    private Context context;

    public Component1ServiceImpl(@NonNull Application app) {
        context = app;
        Toast.makeText(app, "创建了 Component1Service 服务", Toast.LENGTH_SHORT).show();
    }

    @Override
    public Fragment getFragment() {
        return new Component1Fragment();
    }


}

单例服务

@ServiceAnno(value = {Component1Service.class},singleTon = true)
public class Component1ServiceImpl implements Component1Service {

    private Context context;

    public Component1ServiceImpl(@NonNull Application app) {
        context = app;
        Toast.makeText(app, "创建了 Component1Service 服务", Toast.LENGTH_SHORT).show();
    }

    @Override
    public Fragment getFragment() {
        return new Component1Fragment();
    }


}

服务的使用

然后其他任何一个地方就可以这样子使用啦

XXXService service = ServiceManager.get(XXXService.class);
// 然后你可以调用接口中的方法了

或者使用增强版本的 RxServiceManager,可以省去如果服务未找到等情况的判断,并且当你接口中返回的是 RxJava 中的五种Observable,那么其中发射出来的错误你可以不必处理,会自动被忽略,除非你拿到方法返回的Observable还自行使用了其他操作符,这时候请你处理错误的回调

RxServiceManager.with(Component1Service.class)
                .flatMap(new Function<Component1Service, SingleSource<String>>() {
                    @Override
                    public SingleSource<String> apply(Component1Service service) throws Exception {
                        return service.testError();
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<String>() {
                    @Override
                    public void accept(String s) throws Exception {
                        Toast.makeText(TestServiceAct.this, "完成服务的调用啦,内容是:" + s, Toast.LENGTH_SHORT).show();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Toast.makeText(TestServiceAct.this, "可以不用处理的错误,错误信息:" + Utils.getRealThrowable(throwable), Toast.LENGTH_SHORT).show();
                    }
                });

注意事项

1.8.3.5及其以下的版本需要注意这个问题, 以上的版本不做线程限制, 没有此问题

由于本框架的所有有关用户的回调和用户自定义的类的执行线程都是主线程. 所以 Service 的实现类在被创建的时候也是在主线程创建的. 即便你在子线程中调用 ServiceManager.get(xxx.class)

所以有可能会有一种情况. 当主线程和某一个子线程同时争取一个互斥的资源, 子线程获取到了锁, 然后主线程休眠等待. 但是这个子线程调用了 ServiceMamanger.get() 方法, 内部呢又会等待主线程, 造成死锁. 界面显示为空白. 动弹不得. 并且此错误比较难排查.

那么如何规避呢?

  • 尽量事先获取需要使用的 Service, 尽量不要用到的时候再去获取. 比如 Activity, 你在声明这个 Service 的时候就可以初始化它了.
  • 如果自己能决定线程的话, 尽量在主线程去调用 ServiceManager.get() 方法.
  • 发生此情况的几率是很小很小的, 一般不会遇到. 在新版本中为了能帮助用户发生此问题的时候能知道是什么错误, 预计在 1.8.3.5 中会加入等待时间的判断, 如果发生了此情况, 会抛出指定的错误 RunTimeTimeoutException("Component mainThreadCallNullable method timeout"), 这样子用户可以找到调用的地方, 加以解决
Clone this wiki locally