1.爲何要組件化php
2.組件化的概念java
3.建立組件化框架android
4.實際開發案例git
5.組件間通訊程序員
6.關於其餘github
APP迭代維護成本增高sql
多人組合須要組件化數據庫
結合投資界,新芽客戶端分析segmentfault
什麼是組件化呢?api
模塊化
組件化
簡單來講就是提升工做效率,解放生產力,好處以下:
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$$video implements IRouteGroup { @Override public void loadInto(Map<String, RouteMeta> atlas) { 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了。
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);