组件化基础ARouter(二、跨模块API调用)

欢迎访问:juejin.cn/user/330776…
ARouter官方文档:github.com/alibaba/ARo…
组件化ARouter系列(一、启动Activity):juejin.cn/post/704852…
组件化ARouter系列(二、跨模块API调用):juejin.cn/post/705339…
组件化ARouter系列(三、参数传递与解析):juejin.cn/post/705340…

一、ARouter概述

  ARouter是一个用于帮助Android App进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。ARouter的典型应用场景有:

  1. 从外部URL映射到内部页面,以及参数传递与解析;
  2. 跨模块页面跳转,模块间解耦;
  3. 拦截跳转过程,处理登陆、埋点等逻辑;
  4. 跨模块API调用,通过控制反转来做组件解耦;

  本篇主要介绍ARouter的用法之一:跨模块API调用。在组件化中,为了接耦各个模块,一般做法是各个模块之间不直接依赖,改为依赖模块的接口层。

二、ARouter基础用法

  首先,通过一个简单的例子来介绍ARouter-跨模块API调用的基础用法:在MainActivity中有两个文本框,输入数字后点击按钮得到总和:

  上面例子中点击"相加"后,需要调用一个计算模块提供的加法功能,不考虑组件耦合的做法一般是app模块直接依赖计算模块,直接调用计算模块的api:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText editText1;
    private EditText editText2;
    private TextView sumTextView;
    private Button sumButton;

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.main_sum_button) {
            // 获得两个文本框的值
            int a = Integer.parseInt(editText1.getText().toString());
            int b = Integer.parseInt(editText2.getText().toString());
            // Calculate类在calculate模块中,这里直接调用Calculate的api
            Calculate calculate = new Calculate();
            int sum = calculate.sum(a, b);
            sumTextView.setText(Integer.toString(sum));
        }
    }
}
复制代码

  calculate模块中的Calculate类如下:

public class Calculate {
    public int sum(int a, int b) {
        return a + b;
    }
}
复制代码

  例子比较简单,calculate模块提供计算的功能,再假设还有另一个log模块表示日志功能,在这种不考虑耦合的做法中,模块间的依赖关系如下:

  同样地,与第一章"组件化基础ARouter(一)"类似,当项目模块越来越多且依赖关系越来越复杂后,这种方式也会使得项目结构耦合严重或循环依赖等问题。下面介绍ARouter的实现方式。

2.1 添加依赖和配置

apply plugin: 'kotlin-kapt'

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                // ARouter参数
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    implementation 'com.alibaba:arouter-api:1.5.2'
    // 项目中如果使用了kotlin,则需要使用kapt关键字使用Annotation Processor
    // java代码使用annotationProcessor关键字即可
    kapt 'com.alibaba:arouter-compiler:1.5.2'
}
复制代码

2.2 抽象接口服务层

  在组件化的实现方式下,我们需要将模块的功能抽象成一个接口模块,模块间的依赖只依赖接口层,而不依赖具体实现层,这样就达到了组件接耦的目的。如下所示:calculate模块抽象出了一个lib_calculate_interface模块,实现层在lib_calcualte中:

  lib_calculate_interface模块中只有一个继承IProvider的接口,如下所示:

public interface CalculateService extends IProvider {
    public int sum(int a, int b);
}
复制代码

2.3 接口服务实现层

  在实现层lib_calcualte中,CalculateServiceImpl实现了具体功能,并且加上了@Route注解:

@Route(path = "/calculate/sum")
public class CalculateServiceImpl implements CalculateService {
    @Override
    public int sum(int a, int b) {
        return a + b;
    }

    @Override
    public void init(Context context) {
    }
}
复制代码

2.4 跨模块API调用

  可以通过"ARouter.getInstance().build("/calculate/sum").navigation()"来管理&获取服务接口:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText editText1;
    private EditText editText2;
    private TextView sumTextView;
    private Button sumButton;

    // 模块接口
    private CalculateService calculateService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (BuildConfig.DEBUG) {
            ARouter.openDebug();
            ARouter.openLog();
        }
        ARouter.init(getApplication());
        // ARouter方式获取接口实现层实例
        calculateService = (CalculateService) ARouter.getInstance().build("/calculate/sum").navigation();
    }

    @Override
    public void onClick(View v) {
        =if (v.getId() == R.id.main_sum_button) {
            int a = Integer.parseInt(editText1.getText().toString());
            int b = Integer.parseInt(editText2.getText().toString());
            // 调用实现层的sum方法
            int sum = calculateService.sum(a, b);
            sumTextView.setText(Integer.toString(sum));
        }
    }
}
复制代码

  这样,组件化解耦后的实现方式中,由ARouter负责管理和获取服务,模块实现层之间无直接依赖:

三、ARouter源码分析

  下面通过源码来分析ARouter是如何实现第二节中的功能的。

3.1 注解处理

  在2.3中添加注解@Route(path = "/calculate/sum")后,ARouter是使用2.1小节中声明的arouter-compiler来处理注解,自动生成代码,在此基础上实现路由跳转的功能。关于Annotation Processor的基知识可参考:Annotation Processor简单用法
  ARouter APT自动生成三个class文件(位于/lib_calculate/build/generated/source/kapt/debug/com/alibaba/android/arouter/routes目录下):

  这三个class分别实现了IRouteGroup、IRouteRoot、IProviderGroup,且类名都以ARouter$开头,都位于com.alibaba.android.arouter.routes包下:

public class ARouter$$Group$$calculate implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/calculate/sum", RouteMeta.build(RouteType.PROVIDER, CalculateServiceImpl.class, "/calculate/sum", "calculate", null, -1, -2147483648));
  }
}

public class ARouter$$Root$$lib_calculate implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("calculate", ARouter$$Group$$calculate.class);
  }
}

public class ARouter$$Providers$$lib_calculate implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.bc.impl.CalculateService", RouteMeta.build(RouteType.PROVIDER, CalculateServiceImpl.class, "/calculate/sum", "calculate", null, -1, -2147483648));
  }
}
复制代码

  APT自动生成的代码和"组件化基础ARouter(一)3.1小节"的区别在于:
  (1)IRouteGroup中添加的RouteType类型是RouteType.PROVIDER;
  (2)IProviderGroup中新增了往providers中注册的代码;

3.2 SDK初始化

  SDK初始化的源码与"组件化基础ARouter(一)3.1小节"大体类似,不再分析。下面只分析不同的地方LogisticsCenter.init()中,对于模块接口IProvider,以<String,IProviderGroup>添加到HashMap(Warehouse.providersIndex)中:

public class LogisticsCenter {
    private static Context mContext;
    static ThreadPoolExecutor executor;
    private static boolean registerByPlugin;

    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;
        //load by plugin first
        loadRouterMap();
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            // 1.关键代码routeMap
            Set<String> routerMap;
            // It will rebuild router map every times when debuggable.
            // 2.debug模式或者PackageUtils判断本地路由为空或有新版本
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                // 3.获取ROUTE_ROOT_PAKCAGE(com.alibaba.android.arouter.routes)包名下的所有类
                // arouter-compiler根据注解自动生成的类都放在com.alibaba.android.arouter.routes包下
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                // 4.建立routeMap后保存到sp中,下次直接从sp中读取StringSet;逻辑见else分支;
                if (!routerMap.isEmpty()) {
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
                // 5.更新本地路由的版本号
                PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
            } else {
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }
            // 6.获取routeMap后,根据路由类型注册到对应的分组里
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 7.加载root,类名以SUFFIX_ROOT(com.alibaba.android.arouter.routes.ARouter$$Root)开头
                    // 以<String,Class>添加到HashMap(Warehouse.groupsIndex)中
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // 8.加载interceptorMeta,类名以SUFFIX_INTERCEPTORS(com.alibaba.android.arouter.routes.ARouter$$Interceptors)开头
                    // 以<String,IInterceptorGroup>添加到UniqueKeyTreeMap(Warehouse.interceptorsIndex)中;以树形结构实现顺序拦截
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // 9.加载providerIndex,类名以SUFFIX_PROVIDERS(com.alibaba.android.arouter.routes.ARouter$$Providers)开头
                    // 以<String,IProviderGroup>添加到HashMap(Warehouse.providersIndex)中
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }
    }
}
复制代码

3.3 获取服务

3.3.1 ARouter.build()

  继续分析_ARouter.getInstance().build()获取服务时的源码,方法返回Postcard对象,该对象表示一次路由操作所需的全部信息,大体逻辑与"组件化基础ARouter(一)3.1小节"类似,唯一不同的地方在于Postcard对象的RouteType是PROVIDER,在LogisticsCenter完善Postcard的信息时,对于PROVIDER的Postcard对象,会把IProvider对应的实现层实例添加到Postcard对象中:

public class LogisticsCenter {
    public synchronized static void completion(Postcard postcard) {
        // 1.从Warehouse.routes中查找Postcard的path所对应的RouteMeta
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            // routeMet为空,则从groupsIndex查找;没查找到则不存在,查找到则动态添加
        } else {
            // 2.从Warehouse.routes中查找到Postcard所对应的RouteMeta后,完善Postcard信息
            postcard.setType(routeMeta.getType());
            switch (routeMeta.getType()) {
                case PROVIDER:  // if the route is provider, should find its instance
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    // 3.判断Warehouse.providers中是否已经有对应的服务实现层的实例
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            // 4. 如果对应的服务实例不存在,则创建一个
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            logger.error(TAG, "Init provider failed!", e);
                            throw new HandlerException("Init provider failed!");
                        }
                    }
                    // 5. 将服务实现层实例对象赋给postcard
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }
}
复制代码

  如上分析,对于PROVIDER类型的Postcard,LogisticsCenter会返回对应的服务实现层实例对象,特殊提一下,这里的静态方法使用了synchronized标记,来保证多线程操作调用IProvider时取到的对象都是同一个。

3.3.2 Postcard.navigation()

  在_ARouter.getInstance().build()返回Postcard对象后,继续分析navigation()方法,大体逻辑仍与"组件化基础ARouter(一)3.3小节"类似,唯一不同是Postcard的类型是PROVIDER,这里只分析对应的源码,

final class _ARouter {
    /**
     * 根据完善的Postcard,执行对应的路由逻辑
     */
    private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = postcard.getContext();
        // 1.根据不同的routeType执行不同逻辑;我们的例子中routeType是ACTIVITY
        switch (postcard.getType()) {
            case ACTIVITY:
                // ...省略
                break;
            case PROVIDER:
                // 对应PROVIDER类型的Postcard处理很简单,直接返回IProvider的实例即可
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                // ...省略
            case METHOD:
            case SERVICE:
            default:
                return null;
        }
        return null;
    }
}
复制代码

3.4 总结

  1. 在组件化思想中,模块间没有直接依赖,各个模块可以将对外提供的方法抽象成一个服务接口模块,各个模块之间只依赖接口层,而不依赖实现层;
  2. ARouter提供了IProvider接口,模块可以继承IProvider接口来暴露服务;
  3. ARouter提供了ARouter.getInstance().build("/calculate/sum").navigation()来获取path对应的服务;
  4. ARouter源码中对于IProvider服务的处理方式是直接返回对应的实现层实例对象;
  5. 各个IProvider的实例对象存放在Warehouse.providers中,ARouter用synchronized保证获取的对象相同(单例模式);

四、控制反转@Autowired

  在2.4小节是通过"ARouter.getInstance().build("/calculate/sum").navigation()"来获取服务的,此外也可以用ARouter提供的注入功能实现控制反转:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Autowired
    private CalculateService calculateService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (BuildConfig.DEBUG) {
            ARouter.openDebug();
            ARouter.openLog();
        }
        ARouter.init(getApplication());
        // 注入@Autowired标记的变量
        ARouter.getInstance().inject(this);
    }

    @Override
    public void onClick(View v) {
        =if (v.getId() == R.id.main_sum_button) {
            int a = Integer.parseInt(editText1.getText().toString());
            int b = Integer.parseInt(editText2.getText().toString());
            // 调用实现层的sum方法
            int sum = calculateService.sum(a, b);
            sumTextView.setText(Integer.toString(sum));
        }
    }
}
复制代码

  注解@Autowired的原理可参考"组件化基础ARouter(三)"

猜你喜欢

转载自juejin.im/post/7053399480191680520