目前,组件化开发基本已经成为每一个公司必选的开发方案。开源的组件化路由框架也有很多,例如ARouter,CC等,作为一个合格开发人员,熟练的掌握几款开发框架是必须的,网络上对其他的组件路由框架讲解有很多,但今天这篇文章带给大家的是一款新的组件间路由解耦框架--ModuleBus。
简单使用
当然对于一款组件化框架来说想一句代码接入那基本是不可能的,现在我们就来看看ModulesBus是如何接入的。
1.添加依赖和配置
1.工程build.gradle中添加GradlePlugin依赖
repositories {
jcenter()
}
dependencies {
classpath 'com.sogou.module:bus-gradle-plugin:1.2.3'
}
2.模块build.gradle中配置组件化框架的版本
implementation "com.sogou.module:modulebus:1.2.7"
3.配置注解解释器
使用注解编译解释器肯定是为了将自定义注解在编译时期解释成Java代码,方便使用。
所以在需要使用注解的模块中添加注解解释器依赖如下:
如果该模块需要有kotlin支持,在该模块的build.gradle引入kapt插件
apply plugin: 'kotlin-kapt'
向模块的build.gradle文件中dependencies部分添加
implementation "com.sogou.module:annotationProcessor:1.2.0"
//有kotlin支持时使用kapt依赖
//如果不支持kotlin那么使用annotationProcessor依赖
annotationProcessor "com.sogou.module:annotationProcessor:1.2.0"
//kapt "com.sogou.module:annotationProcessor:1.2.0"
4.插件的接入,使用sogouplugin
在每个module的build.gradle中首行添加,并删除
apply plugin: 'com.android.application' 或者 apply plugin: 'com.android.library'
//是主module添加,非主module则不添加
ext.mainApp = true //设置为true,表示此module为主app module,以application方式编译
apply plugin: 'sogouplugin'
5.模块依赖
主module依赖的业务组件使用sogouplugin提供的addComponent方法,可以屏蔽各业务组件对主module的可见性。也就是说使用implementation project(path: ":login")
,主module仍然可以直接引用login模块的代码,但是使用addComponent "login"
,login模块的实现对主module却是不可见的。
2.初始化SDK
当完成上述配置,ModuleBus的sdk其实已经导入。ModuleBus导入sdk的方式并不像其他的类库一样,通过implementation就可以导入,ModuleBus是在sogouplugin插件中自动读取版本号并添加相应的依赖。具体实现
在Application中完成sdk的初始化工作如下:
-
不开启debug模式,不设置全局降级回调,拦截器等
RouterBus.getInstance().init(this);
-
设置debug模式,并设置需要的参数回调
RouterBus.Builder builder = new RouterBus.Builder() .enableDebug(BuildConfig.DEBUG) .addInterceptor(new GlobalInterceptor()) .setGlobalDegrade(new GlobalDegrade() { @Override public void onMissed(RouterBuild build) { Toast.makeText(MyApplication.this, "GlobalDegrade:"+build.getSchema(), Toast.LENGTH_SHORT).show(); } }); RouterBus.getInstance().init(this, builder);
-
参数说明
类型 | 说明 | 备注 |
---|---|---|
enableDebug | Bolean |
true:开启log,跳转Exception抛出,false:以RouterCallback-ResultCode.FAILED 状态回调 |
Interceptor | IInterceptor实现类 | 全局拦截器,会在每次路由跳转时执行 |
GlobalDegrade | GlobalDegrade实现类 | 全局降级接口,在不设置局部回调的情况下,未命中路由会回调此接口 |
RouterFactory | RouterFactory实现类 | 手动设置路由列表 |
3.简单跳转
在完成上述基本配置和初始化后已经成功接入了ModuleBus,接下来看看如何使用它实现不同组件间的跳转吧。
-
目标界面
位于testmodule下的TestMainActivity,在支持路由的页面添加注解,注解没有特殊的格式要求,默认一般使用两级路劲,eg:”activity/test“
@RouterSchema("activity/test")
public class TestMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_main);
}
}
-
跳转操作
在跳转时也可以设置startActivityForResult的回调code,如下:
public void funTestActivity(View view){
RouterBus.getInstance()
.build("activity/test")
//.requestCode(1)
.navigation(this);
}
4.带参跳转
一般的开启新的activity都是需要传递一些参数的,在传递对象行参数时,必须将此对象实现序列化接口。传参方式如下:
- 目标界面
@RouterSchema("activity/param")
class ParamActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_param)
val sb = StringBuilder("参数:\n")
val bundle = intent.extras
if (bundle != null) {
val keySet = bundle.keySet()
val iterator: Iterator<String> = keySet.iterator()
while (iterator.hasNext()) {
val key = iterator.next()
var value: String?
value = if (key == "ObjTest") {
//获取可序列化数据
bundle.getSerializable(key).toString()
} else {
bundle.getString(key)
}
sb.append(key).append(":").append(value).append(";\n")
}
}
param.text = sb.toString();
}
}
- 跳转操作
public void funParamActivity(View view) {
Bundle bundle = new Bundle();
bundle.putString("ModuleBus", "paramTest");
BeanInfo beanInfo = new BeanInfo();
beanInfo.setName("objTest");
RouterBus.getInstance()
.build("activity/param")
.with(bundle)
.with("StringTest", "ParamString")
.with("ObjTest", beanInfo)
.navigation(this);
}
5. 获取fragment
Fragment 传递参数的获取与activity基本相同,获取framgent和跳转的方法如下:
- 目标界面
@RouterSchema("fragment/test")
public class TestFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
- 跳转操作
public void funFragment(View view){
Fragment fragment = RouterBus.getInstance()
.build("fragment/test")
.getFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment).commit();
}
进阶使用
在组件化路由方案中并不单单涉及到avtivity/fragment的跳转,如何开启service也是比较常见的操作。还有比较常见的跳转拦截,启动失败后的降级处理,都是需要考虑的,那么接下来我们看看ModuleBus是如何处理的。
1.Manifest配置跳转
-
跳转操作
新建一个没有界面的UriActivituy用于监听
<a href="test://uriactivity?uri=test/uriactivity">路由分发 test://uriactivity?uri=test/uriactivity</a>
(rul的跳转)事件,之后直接把uri传递给RouterBus作为跳转的Schame完成跳转。<activity android:name=".UriActivituy"> <intent-filter> <!--action.VIEW和category.DEFAULT必须设置--> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <!--如果需要浏览器支持打开,则category.BROWSABLE--> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="test" android:host="uriactivity"/> </intent-filter> </activity>
public class UriActivituy extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Uri uri = getIntent().getData(); if (uri != null){ String schema = uri.getQueryParameter("uri"); RouterBus.getInstance().build(schema).navigation(this); } finish(); } }
-
目标界面
@RouterSchema("test/uriactivity") public class TestUriActivituy extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_uri_activituy); } }
2.自定义全局降级策略
全局的降级策略出现是因为原生的路由方案存在无法灵活降级的问题StartActivity()一旦失败将会抛出运营级异常。当我们并不想应为启动失败而抛出异常,而是去选择执行另一个策略,那么就出现了全局的降级策略。
public class MyGlobalDegrade implements GlobalDegrade {
@Override
public void onMissed(RouterBuild build) {
//dosomething
//eg:
RouterBus.getInstance()
//打开传参测试的activity
.build("activity/param")
//参数设置为原本要打开的Schema
.with("GlobalDegrade", build.getSchema())
.navigation(MyApp.getApp());
}
}
自定义全局降级策略类MyGlobalDegrade,在初始化ModuleBus的时候设置。
3.声明拦截器(拦截跳转过程)
原生的路由方案中存在的问题就是其无法在页面跳转的过程中插入一些自定义逻辑,而拦截器其实就是一种AOP的思想。在跳转过程中处理一些逻辑,例如:一个应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查。拦截器会在跳转之间执行,多个拦截器会按顺序依次执行。这就是拦截器的作用,下面看看ModuleBus是如何使用的:
-
声明拦截器
声明拦截器当当传递条件复合时进行处理,如果不符合那么不处理。
@RouterSchema("/interceptor/test") class TestInterceptor : IInterceptor{ override fun interceptor(p0: IInterceptor.Chain?) { val build: RouterBuild = p0!!.routerBuild build.with("localinterceptor", "Interceptor") val bundle = build.bundle val count = bundle.getInt("count") if (count % 2 == 0) {//符合拦截的条件 val routerCallback = build.callback routerCallback?.result(ResultCode.INTERUPT, build) } else { p0.process() } } }
-
注册拦截器
在设置的callBack中处理具体的拦截逻辑。public void funInterceptor(View view) { IInterceptor interceptor = (IInterceptor) RouterBus.getInstance().build("/interceptor/test").navigation(); RouterBus.getInstance() .build("activity/param") .with("count", 100) .addInterceptor(interceptor) .navigation(this, new RouterCallback() { @Override public void result(int resultCode, RouterBuild build) { switch (resultCode) { case ResultCode.INTERUPT: Toast.makeText(MainActivity.this, "Router INTERUPTED", Toast.LENGTH_SHORT).show(); break; case ResultCode.FAILED: Toast.makeText(MainActivity.this, "Router FAILED", Toast.LENGTH_SHORT).show(); break; } } }); }
4.通过依赖注入解耦:服务管理
由于组件间可能相互调用,但是各个组件又都是项目独立的,所以需要将对外开放使用的对象抽象到base组件,抽象接口:
public interface IService {
public void onNext(Context ctx);
}
- 暴露服务
-
接口暴露法
声明接口,其他组件通过接口来调用服务,在接口暴露时需要额外实现一个标识接口IExported。
- 接口声明
public interface IService { public void onNext(Context ctx); }
- 服务实现和暴露
public class IServiceImpl implements IService, IExported { @Override public void onNext(Context ctx) { Toast.makeText(ctx, "执行onNext", Toast.LENGTH_SHORT).show(); } }
- 服务发现及调用
public void funServiceInterface(View view){ RouterBus.getInstance().navigation(IService.class).onNext(this); }
2.注解暴露法
-
接口声明
public interface IService { public void onNext(Context ctx); }
-
服务实现和暴露
@RouterSchema("service/test") class IServiceImpl2:IService { override fun onNext(ctx: Context?) { Toast.makeText(ctx, "执行onNext2", Toast.LENGTH_SHORT).show() } }
-
服务发现及使用
```
public void funServiceAnnotation(View view){
IService service = (IService) RouterBus.getInstance().build("service/test").navigation();
service.onNext(this);
}
```
5.组件单独运行
上文中我们在插件的接入,使用sogouplugin中讲到,当一个mudule为主组件时我们需要设置ext.mainApp = true
,因此,当我们使用一个mudule单独运行时,需要设置这个参数。具体操作如下:
- 在module的build.gradle文件首行添加 ext.mainApp = true
- 项目local.properties添加主module不需要编译进去的module,例如:
moduletest = true
- 在此module的builde.gradle通过addComponent添加对其他module的依赖,例如:
```groovy
addComponent "moduletest_kt", project(path: ':moduletest_kt')
总结
通过对ModuleBus的学习使用,相信我们对组件化框架的认识会进一步深入。当然ModuleBus的源码结构是非常清晰的,值得我们去学习。github开源地址:组件化解耦框架-ModuleBus
网友评论