http://xiaojinzi.tpddns.cn:18888/post/Android组件化的使用.html
其实组件化方案很多很多,让人选择也是很多很多,但是为什么每一家有技术积累的公司都会参考别人的组件化方案自己去修修改改或者自己实现一套呢?往下看吧
使用开源的库的缺点和优点
缺点
优点
综上所述,一般公司的项目中,在有能力去维护组件化这种对项目整个架构或者整体代码要调整的情况下,自己借鉴或者改造开源项目去贴合项目会更加的好
而且组件中的路由要特别关心,因为当整个项目都可以用路由跳转的时候,那么我们也要兼顾一下 iOS,争取做到 后台下发一些路由 url 来达到跳转一致的效果
组件化的方式很多,但是每一个组件化都必须有这三点,我们做组件化只要保证做到这三点其实就是做了组件化了
项目拆分成多个 module
某个业务组件中的服务如何让别人使用
路由
比如:(实体对象,网络请求模块,数据库,本地存储.....)
业务组件的生命周期(为每一个业务模块带去类似于Application的概念)
业务组件的动态的加载和卸载,比如像大公司的 App,后台能下发指令,下架某一个模块,比如 滴滴 在顺风车出现问题的情况下,下架手机上的顺风车的功能
这就是 '业务组件加载和卸载的生命周期' 可以做到的事情
公司内网:组件化示例项目
github地址:组件化示例项目
这个项目里面实现了组件化的方案,并且使用组件化方案写了一个 Demo,整个项目架构如下:
壳工程
基础库工程
业务组件1
业务组件2
用户业务模块
组件化的注解Api
组件化的注解Api注解驱动器
组件化的基础实现
组件化结合Rxjava2的实现,这个ComponentRxImpl实现中会有一个很好用的功能,基于ComponentApiImpl扩展
对外的发布版本请看 Component,里面有所有的依赖
在主工程的 build.gradle 中添加 maven 地址:
maven { url 'http://192.168.9.230:8081/repository/app-releases/' }
在基础工程 BaseModule 中添加依赖:(版本号你可以先写+,拉下来之后再写死)
api "com.ehi:component-impl:${version}"
或者 RxJava2的实现
api "com.ehi:component-impl-rx:${version}"
各个业务组件会依赖 BaseModule,所以自动会有上述的依赖
然后在各个业务组件中添加注解驱动器
annotationProcessor "com.ehi:component-api-compiler:${version}"
这个会生成各个业务组件的 Application 管理类和路由表
ComponentConfig.init(this,true);
如果依赖了 Rx版本的实现,请调用
EHiRxRouter.tryErrorCatch(); // 这个可以帮助你在路由拿目标界面数据的时候或者整个路由过程中出现的异常可以被忽略,Rxjava2 的错误默认不处理会崩溃哒!!!
EHiModuleManager.getInstance().register("component1");
EHiModuleManager.getInstance().register("component2");
EHiModuleManager.getInstance().register("user");
同样你也可以反注册业务模块,让某一个业务模块下架,比如下架 业务组件1
EHiModuleManager.getInstance().unregister("component1");
@EHiModuleAppAnno()
public class Component1Application implements IComponentApplication {
@Override
public void onCreate(@NonNull final Application app) {
// 你可以做一些当前业务模块的一些初始化
}
@Override
public void onDestory() {
// 你可以销毁有关当前业务模块的东西
}
}
这里展示了一个业务组件1的一个 Activity 的使用
@EHiRouterAnno(
host = "component1",
value = "test",
interceptors = {
Component1Interceptor1.class, Component1Interceptor2.class},
desc = "业务组件1的测试界面"
)
public class Component1TestAct extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.component1_test_act);
TextView tv_data = findViewById(R.id.tv_data);
tv_data.setText(QueryParameterSupport.getString(getIntent(), "data"));
String data = getIntent().getStringExtra("data");
if (data != null) {
tv_data.setText(tv_data.getText() + "\n" + data);
}
}
public void returnData(View view) {
Intent intent = new Intent();
intent.putExtra("data", "this is the return data");
setResult(RESULT_OK, intent);
finish();
}
}
这段就跳转到上面我们写的那个例子的界面中了,并且我们还传了两个 query 值过去哦
EHiRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.navigate();
这段就跳转到上面我们写的那个例子的界面中了,我们传了两个 query 值,并且额外存了两个值到 Bundle 中携带过去,和我们普通的 传值效果是一样的,唯一的区别是 路由的 putXXX 方式利用方法名区别传不同的值, Intent 靠重载方法来传不同的值,但是我更倾向于利用方法区分,因为一旦你入参的参数类型有改动的时候,方法就会报错,而 Intent 不会
Intent intent = new Intent(Context,XXX.class);
intent.putExtra(key,value);
EHiRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.putString("name", "cxj1")
.putInt("age", 25)
.navigate();
EHiRouter.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.navigate(new EHiCallbackAdapter() {
@Override
public void onSuccess(@NonNull EHiRouterResult result) {
// 跳转成功,参数是 request 对象
}
@Override
public void onError(@NonNull Exception error) {
// 跳转失败,参数是 Exception 对象
}
});
你就能从 routerResult 中的 isSuccess 判断是否成功啦!如果失败,routerResult 中的 error 字段也能给你错误的信息
EHiRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.requestCode(456) // requestCode
.navigate();
你就可以在 onActivityResult 方法中拿到数据啦,这种方式和原生的写法没什么差别,骚操作且看下面
EHiRxRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.requestCode(456)
.newIntentCall()
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
// intent 参数中拿数据
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 你可以自己处理错误,如果不传入 错误的回调接口,默认忽略
}
});
这种方式可以说很明显减少了平常写的代码的量,并且让你摆脱了 onActivityResult,所以这可以在** Adapter 或者 任何一个有 Context(其实是一个Activity)的地方去使用**
上述的写法是要处理错误,如果你不想处理错误,可以这样写
EHiRxRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.requestCode(456)
.newIntentCall()
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
// intent 参数中拿数据
}
});
SingleTransformer<String, Intent> transformer = EHiRxRouter
.with(this)
.host("component1")
.path("main")
.query("name", "cxj")
.query("pass", "123")
.requestCode(456)
.intentSingleTransformer();
你可以拿到一个 SingleTransformer,然后把它结合到你任何一个地方,完美的嵌入到一个流程中,不会被 onActivityResult 等方式打断你的流程
在依赖库中有一个设计好的服务容器类
EHiService.class
当你想提供业务组件1的功能出去的时候,你在**基础库(BaseModule)**的 service 包下面新建一个接口文件,这个接口会被所有的业务模块引用
public interface Component1Service {
Fragment getFragment();
void xxx();
int count();
......
}
@EHiServiceAnno(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();
}
}
@EHiServiceAnno(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();
}
}
然后其他任何一个地方就可以这样子使用啦
到此组件化的方案介绍完毕,里面有很多基于得到的经验,也有很多的调整改进和优化
如果有兴趣想更清楚内部的实现,请详细的去琢磨以下三个 module 中的代码吧,不明白的可以来和我交流