-
-
- 组件化为将公共组件抽取为底层,单个功能、产品抽取为单个模块的方式去实现。
- 分拆出来的多个模块即可组合也可单个打包成为独立APP,单独运行,为编译期组合。
- 插件化是以组件化为基础,增加运行时组合拆装能力。
- 两者的区别在于插件化具有组件化所有功能,比组件化多一项运行时组装模块的功能
-
上面提到组件化的单个模块又可组合又可单独运行,这让团队开发实现了分工而为,互不干扰。开发时只编译自己负责的单个模块,编译运行速度将会大大提高,出现模块间耦合的情况将消失!
-
开发时首先将自己负责的模块修改为 asAPP(是否单独运行),然后结合内部Maven库的使用,将所需模块依赖到此模块,进行开发,由于其余支持模块为已编译完成的成品,所以将会大大提高编译此模块的速度,内部Maven库,后面会单独介绍。
-
- BaseModle:包括核心库(三方库等)、各种帮助类以及自定义View
- NetWorK:网络层的操作
- 上层拆分模块,可单独组装运行,合成的APP即为最终的APP
-
- 由于一个APP只有一个Activity入口,所以这里要用一个变量去区分:
在gradle.properties文件中配置:mainmodulename=app //通过Modle名去判断
def asApp = false //是否作为单独的APP this_modle需设置为此modle name if (mainmodulename == "this_modle") { asApp = true } if (asApp) { apply plugin: 'com.android.application' else { apply plugin: 'com.android.library' }
-
-
组件化的第一目标就是 解耦
-
通过scheme和host的匹配关系负责分发路由-这种方式的缺点是定义规则太复杂,维护成本高,出错率较高。
-
通过全局路由的方式,定义对应路由表,通过反射机制去进行对应跳转和携带数据,这里推荐使用阿里开源的ARoute路由库。
- 实现的功能
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
- 支持多模块工程使用
- 支持添加多个拦截器,自定义拦截顺序
- 支持依赖注入,可单独作为依赖注入框架使用
- 支持InstantRun
- 支持MultiDex(Google方案)
- 映射关系按组分类、多级管理,按需初始化
- 支持用户指定全局降级与局部降级策略
- 页面、拦截器、服务等组件均自动注册到框架
- 支持多种方式配置转场动画
- 支持获取Fragment
- 完全支持Kotlin以及混编(配置见文末 其他#5)
- 支持第三方 App 加固(使用 arouter-register 实现自动注册)
- 其主要的应用有:
- 从外部URL映射到内部页面,以及参数传递与解析
- 跨模块页面跳转,模块间解耦
- 拦截跳转过程,处理登陆、埋点等逻辑
- 跨模块API调用,通过控制反转来做组件解耦
- 实现的功能
-
github地址:https://github.com/alibaba/ARouter
-
使用方式:
// 在支持路由的页面上添加注解(必选) // 这里的路径需要注意的是至少需要有两级,/xx/xx @Route(path = "/test/activity") public class YourActivity extend Activity { ... }
// 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中) ARouter.getInstance().build("/test/activity").navigation(); // 2. 跳转并携带参数 ARouter.getInstance().build("/test/1") .withLong("key1", 666L) .withString("key3", "888") .withObject("key4", new Test("Jack", "Rose")) .navigation();
如此简单,只需定义要跳转的类的名字,在任何一个地方任何一个Modle都可以进行跳转,并支持各种方式传参。关键一点是路由表维护可以通过Apt插件去自动生成,自己无需维护,还有更多进阶用法,自己探索。
-
-
资源文件名冲突
defaultConfig { ... resourcePrefix "module_name_" ... }
-
Butterknife R文件问题
@BindView(R2.id.login_tv_title) TextView mTvTitle; @OnClick({R2.id.btn_login}){ //这里只能用 if else 方式,不能使用Swich case 方式 //因为R2文件里的id不是final的case需要final,所以这里使用if else方式去解决此问题 }
最终实现的结果: 由
compile project(':basemodle')
改为
compile 'com.test.android:basecoremodle:1.0.0'
实现代码隔离,防止代码冲突,保证版本的稳定性,引入的为已编译后的代码,加快模块编辑速度。
在modle的build,gradle中添加
apply plugin: 'maven-publish'
apply plugin: 'maven'
uploadArchives {
repositories {
mavenDeployer {
repository(url: "http://127.0.0.1:8081/repository/android/") {
authentication(userName: "admin", password: "admin") //账号,密码
}
pom.project {
packaging 'aar'
description 'modle1'
groupId = GROUP
artifactId = POM_ARTIFACT_ID_LOGIN
version = VERSION_LOGIN
}
}
}
}
重新编译,执行gradle
###封装有三方库
事件总线,传递信息
使用方式:
发送:RxBus2.getInstance().post(“finish_home”);
接收:RxBus2.getInstance().toObservable(String.class)
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
mPresenter.addDispose(d);
}
@Override
public void onNext(@NonNull String s) {
if ("finish_home".equals(s)) {
HomeActivity.this.finish();
}
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
在BaseNetModel或BaseCoreModel初始化时,需传入setSaveFile(true),来控制是否将日志输出到文件保存,待上传服务器。存储位置默认为 cache/log 路径下。
1.项目中的Mlog,应将所有try catch下的异常进行MLog.e(e);输出,用来提供项目异常监测。
2.其余使用方式,请参照MLog使用文档
提供能力:
- currentActivity()--获取当前Activity(堆栈中最后一个压入的)
- finishActivity()--结束当前Activity(堆栈中最后一个压入的)
- finishActivity(Activity activity)--结束指定的Activity
- finishAllActivity()--结束所有Activity
- getActivity(Class<?> cls)--获取指定的Activity
- AppExit(Context context)--退出应用程序
- ......
项目地址:https://github.com/google/gson,项目当前使用版本 "2.8.2"
Gson则是谷歌提供的用来解析Json数据的一个Java类库
// 常用方式
Gson gson = new Gson();
// toJson 将bean对象转换为json字符串
String jsonStr = gson.toJson(user, User.class);
// fromJson 将json字符串转为bean对象
Student user= gson.fromJson(jsonStr, User.class);
注:项目中网络框架已对接口返回Json字符串做了内部解析操作
项目地址:https://github.com/bumptech/glide,项目当前使用版本 "4.6.1"
Glide 4.0版本中取消掉了部分链式布局,但增加的东西是很值得的
例如:在RecyclerView中Glide 自动处理了 View 的复用和请求的取消,传入的 Activity 或 Fragment 实例销毁时,Glide 会自动取消加载并回收资源,等等。
使用方式:
1.在BaseCore中封装有GlideUtils,可以直接实例化其对象进行链式调用。
2.直接使用Glide4.0+提供的注解生成的 GlideApp类来直接进行链式调用(此方法须事先新增MyAppGlideModule类实现@GlideModel注解)
// 常用方式 1
new GlideUtils().with(activity)
.asBitmap()
.load(mDataBeanList.get(position).getImg_url())
.centerCrop()
.placeholder(R.drawable.home_banner_logo)
.error(R.drawable.home_banner_logo)
.into(holder.mIvSelectedPic);
// 常用方式 2 (需build之后才会生成GlideApp类)
GlideApp.with(activity)
.asBitmap()
.load(mDataBeanList.get(position).getImg_url())
.centerCrop()
.placeholder(R.drawable.home_banner_logo)
.error(R.drawable.home_banner_logo)
.into(holder.mIvSelectedPic);
注:使用Glide的时候GlideApp.with(context);建议传入Activity,而不是Application,因为Glide对实例销毁时回收资源已经做了处理,传入Application将会导致Glide的所占用的资源无法被回收。
项目地址:https://github.com/LuckSiege/PictureSelector 项目当前使用版本‘2.2.3’
示例:
PictureSelector.create(CameraActivity.this)
.openGallery(PictureMimeType.ofImage()) //选择方式
.isCamera(false) //是否包含相机选项
.selectionMode(PictureConfig.SINGLE) //选择文件个数
.compress(true) //是否压缩
.enableCrop(true) //是否裁剪
.freeStyleCropEnabled(true) //裁剪框是否可自由移动
.forResult(PictureConfig.CHOOSE_REQUEST); //回调ResultCode
//回调
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == PictureConfig.CHOOSE_REQUEST) {
// 图片、视频、音频选择结果回调
List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data);
// 例如 LocalMedia 里面返回三种path
// 1.media.getPath(); 为原图path
// 2.media.getCutPath();为裁剪后path,需判断media.isCut();是否为true 注意:音视频除外
// 3.media.getCompressPath();为压缩后path,需判断media.isCompressed();是否为true 注意:音视频除外
// 如果裁剪并压缩了,以取压缩路径为准,因为是先裁剪后压缩的
String path = selectList.get(0).getCutPath());
}
}
项目地址:https://github.com/Bigkoo/Android-PickerView 项目当前使用版本‘3.2.7’
//条件选择器
OptionsPickerView pvOptions = new OptionsPickerView.Builder(this, new OptionsPickerView.OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3 ,View v) {
//返回的分别是三个级别的选中位置
String tx = options1Items.get(options1).getPickerViewText()
+ options2Items.get(options1).get(option2)
+ options3Items.get(options1).get(option2).get(options3).getPickerViewText();
tvOptions.setText(tx);
}
}).build();
pvOptions.setPicker(options1Items, options2Items, options3Items);
pvOptions.show();
待补充
待确认全部统一所用
通过全局路由的方式,定义对应路由表,通过反射机制去进行对应跳转和携带数据。
1. 从外部URL映射到内部页面,以及参数传递与解析
2. 跨模块页面跳转,模块间解耦
3. 拦截跳转过程,处理登陆、埋点等逻辑
4. 跨模块API调用,通过控制反转来做组件解耦
使用方式:
// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
...
}
// 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/activity").navigation();
// 2. 跳转并携带参数
ARouter.getInstance().build("/test/1")
.withLong("key1", 666L)
.withString("key3", "888")
.withObject("key4", new Test("Jack", "Rose"))
.navigation();
##目标
-
- 经讨论得出
-
- 通过gradle动态赋值的方式
-
- ARoute路由传递数据
-
- ARoute路由页面跳转
-
-
- 内部Maven库的方式
-
- 拒绝耦合使用路由表的方式加载