本文共 20578 字,大约阅读时间需要 68 分钟。
1.为什么要组件化
2.组件化的概念
3.创建组件化框架
4.实际开发案例
5.组件间通信
6.关于其他
APP迭代维护成本增高
多人组合需要组件化
结合投资界,新芽客户端分析
什么是组件化呢?
模块化
组件化
简单来说就是提高工作效率,解放生产力,好处如下:
1.提高编译速度,从而提高并行开发效率。
2.稳定的公共模块采用依赖库方式
3.每个组件有自己独立的版本,可以独立编译、测试、打包和部署。
5.引用的第三方库代码统一管理,避免版本统一,减少引入冗余库。
组件化和插件化的区别
组件化的目标
在studio中,对两种module进行区分,如下所示
在项目的build.gradle文件中
//控制组件模式和集成模式if (rootProject.ext.isDouBanApplication) { //是Component,可以独立运行 apply plugin: 'com.android.application'} else { //是lib,被依赖 apply plugin: 'com.android.library'}
传统APP架构图
存在的问题
分而治之,并行开发,一切皆组件。要实现组件化,无论采用什么样的技术方式,需要考虑以下七个方面问题:
代码解耦。
组件单独运行。
组件间通信。
组件生命周期。
集成调试。
代码隔离。
组件化架构图
传统以前工程下模块
组件化模式下如何通信
按照理想状态的来看待的话
遇到疑问:
网络解决办法
关于组件化开发一点感想
关于组件化开源项目
主工程:
业务组件:
功能组件:
基础组件:
关于工程中组件依赖结构图如下所示
业务模块下完整配置代码
//控制组件模式和集成模式if (rootProject.ext.isGankApplication) { apply plugin: 'com.android.application'} else { apply plugin: 'com.android.library'}android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] buildToolsVersion rootProject.ext.android["buildToolsVersion"] defaultConfig { minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] if (rootProject.ext.isGankApplication){ //组件模式下设置applicationId applicationId "com.ycbjie.gank" } javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } //jdk1.8 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } sourceSets { main { if (rootProject.ext.isGankApplication) { manifest.srcFile 'src/main/module/AndroidManifest.xml' } else { manifest.srcFile 'src/main/AndroidManifest.xml' } jniLibs.srcDirs = ['libs'] } }}dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':library') annotationProcessor rootProject.ext.dependencies["router-compiler"]}
第一步,首先在项目根目录下创建一个yc.gradle文件。实际开发中只需要更改该文件中版本信息即可。
ext { isApplication = false //false:作为Lib组件存在, true:作为application存在 isAndroidApplication = false //玩Android模块开关,false:作为Lib组件存在, true:作为application存在 isLoveApplication = false //爱意表达模块开关,false:作为Lib组件存在, true:作为application存在 isVideoApplication = false //视频模块开关,false:作为Lib组件存在, true:作为application存在 isNoteApplication = false //记事本模块开关,false:作为Lib组件存在, true:作为application存在 isBookApplication = false //book模块开关,false:作为Lib组件存在, true:作为application存在 isDouBanApplication = false //豆瓣模块开关,false:作为Lib组件存在, true:作为application存在 isGankApplication = false //干货模块开关,false:作为Lib组件存在, true:作为application存在 isMusicApplication = false //音乐模块开关,false:作为Lib组件存在, true:作为application存在 isNewsApplication = false //新闻模块开关,false:作为Lib组件存在, true:作为application存在 isToDoApplication = false //todo模块开关,false:作为Lib组件存在, true:作为application存在 isZhiHuApplication = false //知乎模块开关,false:作为Lib组件存在, true:作为application存在 isOtherApplication = false //其他模块开关,false:作为Lib组件存在, true:作为application存在 android = [ compileSdkVersion : 28, buildToolsVersion : "28.0.3", minSdkVersion : 17, targetSdkVersion : 28, versionCode : 22, versionName : "1.8.2" //必须是int或者float,否则影响线上升级 ] version = [ androidSupportSdkVersion: "28.0.0", retrofitSdkVersion : "2.4.0", glideSdkVersion : "4.8.0", canarySdkVersion : "1.5.4", constraintVersion : "1.0.2" ] dependencies = [ //support "appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}", "multidex" : "com.android.support:multidex:1.0.1", //network "retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}", "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}", "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}", //这里省略一部分代码 ]}
第二步,然后在项目中的lib【注意这里是放到基础组件库的build.gradle】中添加代码,如下所示
apply plugin: 'com.android.library'android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] buildToolsVersion rootProject.ext.android["buildToolsVersion"] defaultConfig { minSdkVersion rootProject.ext.android["minSdkVersion"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] }}dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) api rootProject.ext.dependencies["appcompat-v7"] api rootProject.ext.dependencies["design"] api rootProject.ext.dependencies["palette"] api rootProject.ext.dependencies["glide"] api (rootProject.ext.dependencies["glide-transformations"]){ exclude module: 'glide' } annotationProcessor rootProject.ext.dependencies["glide-compiler"] api files('libs/tbs_sdk_thirdapp_v3.2.0.jar') api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" //省略部分代码}
第三步,在其他model中添加依赖
基础库组件封装
组件初始化
如何简化不熟悉组件化的人快速适应组件独立运行
严格限制公共基础组件的增长
在玩Android组件下的build.gradle文件,其他组件类似。
//控制组件模式和集成模式if (rootProject.ext.isAndroidApplication) { apply plugin: 'com.android.application'} else { apply plugin: 'com.android.library'}
集成模式如下所示
ext { isAndroidApplication = false //false:作为Lib组件存在, true:作为application存在
组件模式如下所示
ext { isAndroidApplication = true //false:作为Lib组件存在, true:作为application存在
需要注意的地方,这个很重要
首先看看网上绝大多数的作法,非常感谢这些大神的无私奉献!但是我觉得多个组件用一个开关控制也可以,但是sourceSets里面切换成组件app时,可以直接不用下面这么麻烦,可以复用java和res文件。
android { defaultConfig { if (rootProject.ext.isAndroidApplication){ //组件模式下设置applicationId applicationId "com.ycbjie.android" } } sourceSets { main { if (rootProject.ext.isAndroidApplication) { manifest.srcFile 'src/main/module/AndroidManifest.xml' } else { manifest.srcFile 'src/main/AndroidManifest.xml' } jniLibs.srcDirs = ['libs'] } }}
重复依赖问题说明
解决办法,举个例子
api(rootProject.ext.dependencies["logger"]) { exclude module: 'support-v4'//根据组件名排除 exclude group: 'android.support.v4'//根据包名排除 }
业务组件之间联动导致耦合严重
组件化开发之数据库分离
资源名冲突有哪些?
解决办法
个人建议
如何做到各个组件化模块能获取到全局上下文
情景再现
解决办法
butterKnife使用问题
当组件化是lib时
不要乱发bus消息
页面跳转存在问题
关于跳转参数问题
先来看一下这种代码写法,这种写法本没有问题,只是在多人开发时,如果别人想要跳转到你开发模块的某个页面,那么就容易传错值。建议将key这个值,写成静态常量,放到一个专门的类中。方便自己,也方便他人。
//跳转intent.setClass(this,CommentActivity.class);intent.putExtra("id",id);intent.putExtra("allNum",allNum);intent.putExtra("shortNum",shortNum);intent.putExtra("longNum",longNum);startActivity(intent);//接收Intent intent = getIntent();int allNum = intent.getExtras().getInt("allNum");int shortNum = intent.getExtras().getInt("shortNum");int longNum = intent.getExtras().getInt("longNum");int id = intent.getExtras().getInt("id");
比较有代表性的组件化开源框架有得到得到DDComponentForAndroid、阿里Arouter、聚美Router 等等。
这里只是说一下基础的思路
在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址。
/** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */public class ARouter$$Group
implements IRouteGroup {
@Override public void loadInto(Mapatlas) { atlas.put("/video/VideoActivity", RouteMeta.build(RouteType.ACTIVITY, VideoActivity.class, "/video/videoactivity", "video", null, -1, -2147483648)); }}
- ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。- 看_ARouter类中的 _navigation方法代码,在345行。
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = null == context ? mContext : context; switch (postcard.getType()) { case ACTIVITY: // Build intent final Intent intent = new Intent(currentContext, postcard.getDestination()); intent.putExtras(postcard.getExtras()); // Set flags. int flags = postcard.getFlags(); if (-1 != flags) { intent.setFlags(flags); } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Set Actions String action = postcard.getAction(); if (!TextUtils.isEmpty(action)) { intent.setAction(action); } // Navigation in main looper. runInMainThread(new Runnable() { @Override public void run() { startActivity(requestCode, currentContext, intent, postcard, callback); } }); break; case PROVIDER: //这里省略代码 case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: //这里省略代码 case METHOD: case SERVICE: default: return null; } return null;}
使用阿里路由抽取工具类,方便后期维护!
首先看一下网络上有一种写法。
//首先通过注解添加下面代码@Route(path = "/test/TestActivity")public class TestActivity extends BaseActivity {}//跳转ARouter.getInstance().inject("/test/TestActivity");
优化后的写法
//存放所有的路由路径常量public class ARouterConstant { //跳转到视频页面 public static final String ACTIVITY_VIDEO_VIDEO = "/video/VideoActivity"; //省略部分diamagnetic}//存放所有的路由跳转,工具类public class ARouterUtils { /** * 简单的跳转页面 * @param string string目标界面对应的路径 */ public static void navigation(String string){ if (string==null){ return; } ARouter.getInstance().build(string).navigation(); }}//调用@Route(path = ARouterConstant.ACTIVITY_VIDEO_VIDEO)public class VideoActivity extends BaseActivity {}ARouterUtils.navigation(ARouterConstant.ACTIVITY_VIDEO_VIDEO);
转载地址:http://xamhl.baihongyu.com/