最近由於業務需求變動,有考慮採用組件化架構進行開發,這方面我以前沒有接觸過。關於組件化的文章不少,各方大神更是提出了各類的組件化方案,我也看了不少相關文章。可是學習新東西看的再多,不如動手作一次,先不考慮複雜的東西,先動手作個簡單的Demo更有助於理解組件化的思想。組件化相關理論知識這裏就很少講了,想要了解的能夠本身去搜或者去看Android組件化方案這篇文章。廢話很少說,直接動手開碼。java
先打開Android Studio新建一個項目。android
而後在項目目錄下新建一個config.gradle文件。 git
ext { //表示是做爲module仍是application isModule = false //applicationId版本好sdkVersion統一管理 android = [ compileSdkVersion : 28, buildToolsVersion : 28, applicationId : "com.example.componenttestdemo", minSdkVersion : 19, targetSdkVersion : 28, versionCode : 1, versionName : "1.0", testInstrumentationRunner: "android.support.test.runner.AndroidJUnitRunner" ] //版本號 def APPCOMPAT_V7_VERSION = "28.0.0" def CONSTRAINT_LAYOUT_VERSION = "1.1.3" //三方庫統一管理 dependencies = [ appcompatV7 : 'com.android.support:appcompat-v7:' + APPCOMPAT_V7_VERSION, constraintLayout: 'com.android.support.constraint:constraint-layout:' + CONSTRAINT_LAYOUT_VERSION ] } 複製代碼
由於咱們知道項目使用組件化架構後,單一模塊Module
能夠做爲單個Application
運行,同時也能夠在整個主Application
中做爲一個Module
運行。因此在config.gradle
中先定義一個isModule
來區別這兩種狀況,組件化以後能夠經過修改這個值來切換這兩種狀況的使用。剩下就是對applicationId
、版本號、sdkVersion
和三方庫等進行統一管理。github
接着修改app下的build.gradle裏設置內容安全
將原來的compileSdkVersion
、applicationId
、minSdkVersion
、versionCode
和三方庫等替換成對應config.gradle中定義的值。bash
apply plugin: 'com.android.application' android { compileSdkVersion rootProject.ext.android.compileSdkVersion defaultConfig { applicationId rootProject.ext.android.applicationId minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName testInstrumentationRunner rootProject.ext.android.testInstrumentationRunner } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation rootProject.ext.dependencies.appcompatV7 implementation rootProject.ext.dependencies.constraintLayout } 複製代碼
最後還要在項目目錄下的build.gradle中添加一行:服務器
apply from : "config.gradle" 複製代碼
Sync Now
同步。最後在進行下一步前,新建一個MyApplication,在
AndroidManifest
設置name屬性。
咱們知道組件化中須要一個app殼工程,這個殼工程中不處理任何業務,就只是一個空殼,由它將所須要的各個組件模塊組合起來,構成一個完整的應用。而如今項目中的app仍是存在默認的入口Activity
的,因此要新建一個ModuleMain
將默認的MainActivity
和其佈局文件搬過去。markdown
接着進入app的AndroidManifest
文件將註冊Activity的相關代碼也搬到ModuleMain
模塊的AndroidManifest
中去,只留下application
標籤。架構
這裏注意組件化項目中每一個Module都會有本身的AndroidManifest
文件,最後打包時會將這些文件合併成一個文件,因此會出現application
標籤中的屬性重複問題,要在app的AndroidManifest
文件中添加以下兩行代碼:app
xmlns:tools="http://schemas.android.com/tools" 複製代碼
tools:replace="android:name,android:label,android:icon, android:theme,android:allowBackup" 複製代碼
這裏的name
、label
、icon
、theme
、allowBackup
均可能會有重複,因此所有寫上之間用逗號隔開。完整AndroidManifest
以下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sy.modulesimpledemo" xmlns:tools="http://schemas.android.com/tools" > <application android:name=".application.MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" tools:replace="android:name,android:label,android:icon, android:theme,android:allowBackup" android:theme="@style/AppTheme"> </application> </manifest> 複製代碼
接着app殼工程中只剩剛修改的build.gradle
還沒刪減,在刪減前先將app中build.gradle
的內容複製覆蓋到Main模塊的build.gradle
中,而且還要作部分修改。由於單個組件能夠做爲一個組件模塊被app殼工程組合使用,也能夠單獨做爲一個application
使用。因此要根據config.gradle
中定義的isModule
來判斷是做爲Module仍是Applicaition。一樣還有做爲Module是不須要applicationId
的而做爲應用則是須要的。
//經過isModule來判斷是application仍是module if (rootProject.ext.isModule) { apply plugin: 'com.android.library' } else { apply plugin: 'com.android.application' } android { compileSdkVersion rootProject.ext.android.compileSdkVersion defaultConfig { //是application才須要applicationId if (!rootProject.ext.isModule) { applicationId "com.example.sy.moduledmain" } minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName testInstrumentationRunner rootProject.ext.android.testInstrumentationRunner } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation rootProject.ext.dependencies.appcompatV7 implementation rootProject.ext.dependencies.constraintLayout } 複製代碼
這時modulemain中的AndroidManifest
會提示資源文件的缺乏,這時先將app中的對應文件複製到modulemain裏來。
Application
和AndroidManifest
文件在app殼工程和ModuleMain中分別新建一個Application
,由於Moudule也是須要能夠單獨運行的。
AndroidManifest
文件,由於做爲
Module
和
Application
是會有不同的因此要作區分,在main目錄下新建module文件夾和application文件夾分別存放兩個狀況下的
AndroidManifest
文件,將原來的
AndroidManifest
文件拖到module下,再拷貝一份到application下。拷貝完了記得在兩個
AndroidManifest
裏
application
標籤下設置name屬性。
接着再build.gradle
中添加以下代碼,用來分別在兩種狀況下指定使用哪一個AndroidManifest
。
sourceSets { main { if (rootProject.ext.isModule) { manifest.srcFile 'src/main/module/AndroidManifest.xml' } else { manifest.srcFile 'src/main/application/AndroidManifest.xml' java { //排除java/module文件夾下的全部文件 exclude '*module' } } } } 複製代碼
而後再到app的build.gradle
中在dependencies
內添加如下代碼,用來引入ModuleMain模塊。
if (rootProject.ext.isModule) { implementation project(":modulemain") } 複製代碼
如今能夠再次點擊Sync Now
等同步結束後,雖然項目中只有一個殼工程和一個主Module,可是已能夠看到組件化的雛形。此時已經能夠經過修改config.gradle
裏的isModule
的值,進行Application
和Module
兩種模式的切換,將ModuleMain做爲app 的模塊運行或者是單獨做爲一個應用運行了。
接着按照新建ModuleMain的步驟重複新建其餘業務Module,這裏我新建了3個Module,業務A:ModuleA與業務B:ModuleB和一個BaseModule。其中BaseModule主要存放一些基礎類和工具類,只作爲Module爲上層業務模塊提供服務。
build.gradle
添加下面這行代碼,爲資源文件命名規範一個統一開頭:
resourcePrefix "modulemain_" 複製代碼
添加後起名是沒按照規範Android Studio
就會有一個提示:
接下來就要處理組件間的通訊問題,採用阿里的ARouter。按照文檔集成ARouter。 首先在defaultConfig
下添加以下代碼:
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
複製代碼
再引入ARouter
依賴:
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.arouterCompiler
複製代碼
最後在Application
中初始化ARouter
:
if (isDebug()) { // 這兩行必須寫在init以前,不然這些配置在init過程當中將無效 ARouter.openLog(); // 打印日誌 ARouter.openDebug(); // 開啓調試模式(若是在InstantRun模式下運行,必須開啓調試模式!線上版本須要關閉,不然有安全風險) } ARouter.init(mApplication); // 儘量早,推薦在Application中初始化 複製代碼
這樣ARouter
就集成好了,接着在MoudleA和ModuleB中新建兩個Activity
,而後使用ARouter
進行頁面跳轉。
ModuleMain中MainActivity.java:
public class MainActivity extends BaseActivity { /** * toA */ private Button mModulemainA; /** * toB */ private Button mModulemainB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.modulemain_activity_main); initView(); initEvent(); } private void initEvent() { mModulemainA.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ARouter.getInstance().build(ARouterPath.PATH_MOUDULE_A).navigation(); } }); mModulemainB.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ARouter.getInstance().build(ARouterPath.PATH_MOUDULE_B).withString("key","傳遞的數據").navigation(); } }); } private void initView() { mModulemainA = (Button) findViewById(R.id.modulemain_a); mModulemainB = (Button) findViewById(R.id.modulemain_b); } } 複製代碼
ModuleA中ModuleAActivity.java:
@Route(path = ARouterPath.PATH_MOUDULE_A) public class ModuleAActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.modulea_activity_module_a); } } 複製代碼
ModuleB中ModuleBActivity.java:
@Route(path = ARouterPath.PATH_MOUDULE_B) public class ModuleBActivity extends AppCompatActivity { @Autowired(name = "key") String data; /** * TextView */ private TextView mTextViewB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.moduleb_activity_module_b); ARouter.getInstance().inject(this); initView(); } private void initView() { mTextViewB = (TextView) findViewById(R.id.textViewB); mTextViewB.setText(data); } } 複製代碼
運行效果:
在開發中,可能會把一些公用Module傳到私有服務器上,而後在項目中直接依賴使用。下面就將Module上傳到Github做爲遠程maven倉庫,在項目直接引用。首先新建一個項目,建立一個UtilModule。
將原來項目中的工具類移到UtilModule中,接着在UtilModule的build.gradle
中添加如下代碼:
apply plugin: 'maven' uploadArchives { repositories.mavenDeployer { def mavenDirPath = file('\\Users\\sy\\AndroidProjects\\UtilModule') // 本地存放地址 repository(url:"file://${mavenDirPath.absolutePath}") pom.project { groupId "com.example.utilmodule" // 包名 artifactId "utilmodule" // module的名字 version "1.0.0" // 版本號 } } } 複製代碼
而後點擊gradle
中的uploadArchives
:
進入設置的目錄查看,aar已經打包好了。
接着打開Github建立一個新倉庫:
按照Github上的命令,將本地打包好的UtilModule上傳到Github上:
github.com
部分修改成
raw.githubusercontent.com
再在結尾加上
/master
表示是主分支,添加到項目中的
build.gradle
中。
build.gradle
中添加依賴:
utilmodule : 'com.example.utilmodule:utilmodule:' + UTIL_MODULE_VERSION 複製代碼
implementation rootProject.ext.dependencies.utilmodule
複製代碼
這裏就是以前設置的包名:Module名:版本號。SyncNow
以後刪除原來項目中的工具類,而後在代碼裏使用遠程倉庫的工具類測試:
D/com.example.modulemain.MainActivity: onCreate:false 複製代碼
這說明遠程倉庫依賴成功已經能正常使用其中的類和方法。
這篇文章主要是記錄下我初識組件化,搭建組件化Demo的過程,Demo主要對於我對組件化思想的理解和體驗仍是頗有幫助的,Demo中還有不少沒考慮到的地方,好比Application的動態配置合併、Fragment、組件化的混淆等等,也是我正在學習的問題。這篇文章主要供和我同樣對組件化這塊不太瞭解的新手作參考,但願能對新手有所幫助。
最後再貼上幾個總結得比較好的開源組件化方案的連接,也是我後面準備研究學習的方案:
多個維度對比一些有表明性的開源android組件化開發方案
AppJoint方案
ArmsComponent方案
補一下文中Demo
源碼地址:github.com/syDeveloper…