一.隐式跳转:
在组件化中,两个功能模块时不存在直接依赖的,其依赖规则时通过Base module简洁依赖的。一般的直接跳转是从一个Activity跳转到另一个Activity,使用startActivity发送一个包装好的intent,将intent交给ActivityManagerService完成新的Activity创建。但是当包装intent时,如果发现不了引用不了其他module中的Activity类,则无法索引到Activity类。
在不同的module之间跳转Activity的intent的隐式跳转时,先在AndroidManifest中注册intent,然后在其他module使用隐式action跳转到相应的Activity。值得注意的是使用app包名+类名跳转的方式。
Intent intent = new Intent();
intent.setClassName("App包名","activity路径");
intent.setComponent(new ComponentName("App包名","activity路径"));
startActivity(intent);
如移除activity所在module,而不移除跳转,activity会出现奔溃异常。在Android官网提示,使用隐式intent跳转需要验证时都会接收到intent,需要对intent对象调用resolveActivity(),如结果为非空,则至少有一个应用能够处理该intent,且可以安全调用startActivity;如为空则不使用该intent。
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
还有对于安全问题的考虑,其他App也可以通过隐式的intent来启动Activity。确保只有自己的app能启动组件需要exported = false,其他app将无法跳转到我们的app中。
二.ARouter路由跳转:
路由的概念广泛运用在计算机网络中,指路由器从一个接口上接收到数据包,根据数据路由包的目的地址进行定向并转发到另一个接口的过程。路由器用于连接多个逻辑分开的网络。我们可以将各个组件module看作一个个不同的网络,而Router就是连接各个模块中页面跳转的中转站,这个中转站可以拦截不安全的跳转或者设定一些特定的拦截服务。
原生跳转和Route跳转的差异:
- 显示跳转需要依赖于类,而路由跳转通过URL索引,无须依赖;
- 隐式通过AndroidMainfest集中管理,协作开发困难;
- 原生需要在AndroidMainfest中注册,而路由用注解来注册;
- 原生只要启动了startActivity就交给Android控制,而路由使用AOP切面编程可以进行控制跳转的过滤;
这样的对比明显路由非常适合组件化解耦。
- 首先需要在Base module中添加一些配置:
compile引用aouter-api库,annotationProcessor是apt注解框架的声明。
compile 'com.alibaba:arouter-api:1.1.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.1'
- 然后annotationProcessor会使用javaCompileOption这个配置来获取当前module的名字,在各个模块中的build.gradle的defaultConfig属性中加入:
javaCompileOptions {
anntationProcessorOptions {
arguments = [moduleName : project.getName]
}
}
- 每个模块的dependencies属性需要ARouter apt的引用,不然无法在apt中生成索引文件无法跳转:
dependencies {
annotationProcessor 'com.alibaba:arouter-compiler:1.1.1'
}
- 在Application进行初始化:
if (isDebug()) { // These two lines must be written before init, otherwise these configurations will be invalid in the init process
ARouter.openLog(); // Print log
ARouter.openDebug(); // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
}
ARouter.init(mApplication); // As early as possible, it is recommended to initialize in the Application
- ARouter的使用,以web模块为例,WebActivity需要添加注解Router,path是跳转的地址:
@Router(path = "/gank_web/1")
public class WebActivity extends BaseActivity
使用ARouter跳转,build处填写地址,withXXX处填的是参数的key和value,navigation就是发射了路由跳转,建造者模式:
ARouter.getInstance().build("地址")
.withString("url",url)
.withString("title",desc)
.navigation();
webActivity通过读取传递的intent的方式就可以获取参数:
baseIntent = getIntent();
String title = baseIntent.getStringExtra("title");
String url = baseIntent.getStringExtra("url");
三.Android路由器原理:
ARouter拥有自身的编译时注解框架,编译器会根据注解生成三个文件,用于每个模块页中的页面路由索引。生成的三个文件继承于ARouter接口。
在Application加载的时,ARouter会使用初始化调用init的方法,之后在LogisticsCenter中通过加载编译时注解时创建Group/Providers/Root三个类型的文件,使用Warehouse将文件保存在三个不同的HashMap对象中,Warehouse相当于路由表,其保存着全部的模块跳转关系。
- 通过ARouter.navigation封装postcard对象;
- 通过ARouter索引传递到LogisticsCenter(路由中转站),询问是否存在跳转的对象;
- 如存在则设置绿色通道开关;
- 判断是否绿色通行和是否能通过拦截服务;
- 全部通过就会调用ActivityCompat.startActivity的方法跳转到目的activity。
可以看出路由的跳转实际上还是调用了ActivityCompat.startActivity的跳转,使用了原声的Framework机制,只是通过apt注解的形式制造出跳转规则,并人为的拦截跳转和设置跳转条件。
四.组件化最佳路由:
在组件化基础架构中,假如移除一些功能module和跳转关系,则无法跳转到那些移除module的页面,此时如果要做一些提示,将嵌入更多的判断机制的代码。而使用路由机制,可以统一对这些索引不到module的页面进行提前拦截和作出提示。路由器除了跳转,另一个非常大的作用时拦截。使用拦截过滤,可以在跳转前进行登录状态的验证拦截。
现在的开源框架软件中有ActivityRouter/天猫统跳协议/ARouter/DeepLinkDispatch/OkDeepLink,如项目中没有引入RxJava,那么ARouter接入成本低,且框架可控性高是首选;如项目中引入RxJava则有一款很好的路由选择OkDeepLink。
空类索引:
空类索引可以作为组件化跳转的终极技巧,其优势在于使用原生的跳转函数就可以实现,缺点在于并没有路由拦截等功能,跳转前需要检查activity索引是否存在,不然有奔溃的危险。
空类索引的核心原理就是使用空类来欺骗编译:
- 四大组件需要时使用AndroidMainfest来注册配置;
- 编译时,先编译Library module,最后会将每个Library module的代码和资源汇总到app module,再打包成apk;
- 可以通过provide方式来将库文件引用到编译中,但实际上不会将其编译到代码中,只存在引用关系。