關於什麼是組件化、爲何要進行組件化以及實施組件化的基本流程網上一搜一大把,這裏不作過多說明,不瞭解的話能夠Google一下。這裏主要記錄一下組件化開發的一些心得和踩的一些坑。vue
在gradle.properties文件裏定義一個常量IsBuildApp = false,表示是否把組件module做爲單獨的app運行。定義好了這個常量後,在項目的任何一個gradle文件裏均可以讀取到這個值,那麼就用這個值來做爲module組件是否須要單獨運行的開關。java
// 在module組件的gradle裏配置以下,gradle.properties 中的數據類型都是String類型,這裏須要作一下轉換
if (IsBuildApp.toBoolean()){
apply plugin: 'com.android.application'
}else {
apply plugin: 'com.android.library'
}
複製代碼
咱們知道android的四大組件、權限等都是須要註冊的,當module單獨運行的時候,確定須要一個清單文件註冊組件和申請權限,可是當module做爲app的一個子組件存在的時候,清單文件是要合併到app的殼工程中的,這個時候若是每一個module都有本身的啓動頁面和自定義application的話,就會引發衝突。android
爲了解決這個問題,那就須要根據module是否須要單獨運行來配置不一樣的清單文件。在java同級目錄新建independent目錄,在此目錄下建立項目module須要單獨運行的清單文件和application。而後在module的gradle文件裏指定清單文件路徑,代碼以下:git
// 在android領域裏指定清單文件的路徑
sourceSets {
main {
if (IsBuildApp.toBoolean()) {
// 單獨做爲app運行的清單文件,這裏能夠添加啓動頁面、自定義application等。
manifest.srcFile 'src/main/independent/AndroidManifest.xml'
} else {
// 做爲組件的清單文件
manifest.srcFile 'src/main/AndroidManifest.xml'
//release模式下排除independent文件夾中的全部Java文件
java {
exclude 'independent/**'
}
}
}
}
複製代碼
這樣配置完成之後,做爲組件的清單文件是不能有本身的啓動頁面、application、appname等屬性的,下面看一下完整的配置:github
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.article.demos.vue">
<application android:theme="@style/AppTheme">
<activity android:name=".ui.VueActivity" />
</application>
</manifest>
複製代碼
下面看一下獨立運行模式下的清單文件:api
// 做爲獨立app運行的清單文件,注意這裏我設置了主題,否則的話會報錯。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.article.demos.main">
<application android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
複製代碼
獨立運行的話,就和正常的app清單文件同樣,要有啓動頁面,application標籤能夠添加label、icon、自定義application等,就很少說啦。微信
在commonlibrary中建立自定義application,由於其餘的module都依賴這個module,因此其餘的module均可以獲取到這個全局的application。另外,組件在獨立運行模式下的application,繼承咱們自定義這個BaseApplication就能夠了。由於咱們在release模式下,排除了全部independent文件夾下的java文件,因此做爲組件運行時,並不會產生application的衝突,配置以下:架構
sourceSets {
main {
if (IsBuildApp.toBoolean()) {
manifest.srcFile 'src/main/independent/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//release模式下排除independent文件夾中的全部Java文件
java {
exclude 'independent/**'
}
}
}
}
複製代碼
爲了不重複依賴三方庫的問題,咱們的三方庫依賴統一放在commonlibrary的module中,這樣既能夠避免重複依賴,又方便管理。而後咱們在app的module裏,以下引用便可:app
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
if (IsBuildApp.toBoolean()) {
implementation project(':commonlibrary')
} else {
implementation project(':androidmodule')
implementation project(':vuemodule')
implementation project(':kotlinmodule')
implementation project(':javamodule')
}
}
複製代碼
資源衝突主要是指各個module裏的資源文件名衝突的問題,若是命名同樣,合併的時候便會產生衝突。ide
解決衝突主要有兩個解決方案,一個是約定規則,好比資源名約定都以module名開頭。
方案二是經過gradle腳原本設置,在各個組件的gradle文件裏添加以下代碼:
resourcePrefix "module名稱_"
複製代碼
可是這種配置有限制,好比只能限定xml裏的資源,因此並不推薦這種方式。
由於組件是相互隔離的,咱們並不能顯式跳轉,這裏咱們選用阿里巴巴的Arouter路由跳轉,項目的地址github.com/alibaba/ARo…。
這裏須要特別說明一下,須要跳轉的目標module須要引入arouter的註解處理器,不然沒法處理router註解會出現路徑不匹配的問題:
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
複製代碼
同時,改module的defaultconfig裏也別忘記配置moduleName
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
複製代碼
跨moduel交互通常是指module間通訊和module間的相互調用。module間通訊這裏選用eventbus,很簡單,就不過多說明了。
下面說一下同級module直接的通訊,好比我在任何一個頁面要調用loginModule裏的微信登陸方法,由於各個module是互相獨立的,互不依賴,想要直接調用基本不可能。目前網上發現有兩種解決方案,一個是寫一個反射工具類,經過反射獲取到要調用的類,而後調用相應的方法。另外一個是經過commonModule作一下橋接,瞭解更多能夠參考這裏。不過感受用Arouter能更優雅的實現,下面具體講一下利用arouter來實現。
首先,在公共module裏建立一個接口IService
public interface IService extends IProvider{
String wxLogin();
}
複製代碼
接口裏定義一個微信登陸的僞代碼,而後在咱們的登陸組件裏,實現該接口並添加route註解
@Route(path = Constant.WX_LOGIN)
public class WxTest implements IService{
@Override
public void init(Context context) {
}
@Override
public String wxLogin() {
return "wxlogin";
}
}
複製代碼
其中 Constant.WX_LOGIN是我定義的一個字符串常量
public static final String WX_LOGIN = "/wx/login";
複製代碼
以上兩步就把工做作完了,下面只須要在須要調用的頁面調用登陸就好了。首先,咱們獲取到IService
/**
* 推薦使用方式二來獲取IService
*/
// IService iService = (IService) ARouter.getInstance().build(Constant.WX_LOGIN).navigation();
IService iService = ARouter.getInstance().navigation(IService.class);
複製代碼
拿到IService後,就能夠放心大膽的調用登陸方法就好了。
mBinding.btLogin.setOnClickListener(v -> {
String s = iService.wxLogin();
Toast.makeText(getContext(), s, Toast.LENGTH_SHORT).show();
});
複製代碼
通常的項目首頁都是一個activity和多個fragment組成。因爲組件間的隔離,咱們在首頁裏怎麼獲取到其餘組件裏的fragment呢?開篇的兩個參考文章分別使用了兩種不一樣的方式,有興趣的朋友能夠看看。各有利弊吧,一個是查詢全部,太耗時。一個是直接反射獲取,可是好像有點違背組件隔離,須要知道fragment的全路徑。
這裏我參考了《Android組件化架構》一書,使用arouter來獲取。其實三種方式獲取的原理同樣,都是經過反射。咱們看一下arouter的註解的源碼就知道:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {……}
複製代碼
能夠看到Route註解的retention是CLASS,也是經過反射來獲取。
(1)使用dataBinding的話,每一個module的gradle文件裏都要加上dataBinding的支持,不然沒法生成相應的binding類
// 每一個module都加上dataBinding的支持,不然沒法生成相應的binding類
dataBinding {
enabled = true
}
複製代碼
(2)java8的支持同樣要每一個module都要單獨配置
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
複製代碼
(3)升級到as 3.1.2後,出現沒法訪問TaskStackBuilder的問題
檢查一下你的support包,將你的support包更新到27或以上便可。
(4)若是使用有自定義註解annotation的話,若是編譯報錯 Annotation processors must be explicitly declared now...,那麼在commonlibrary的gradle文件的defaultConfig裏添加以下代碼:
// Annotation processors must be explicitly declared now
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
複製代碼
(5)若是你組件化開發,子module中沒法使用butterknife的話,網上自行搜解決方案吧(🤦♀️)
關於爲什麼出現這個問題,推薦一篇博文R.java、R2.java是時候懂了
(6)其餘問題本篇博客會持續更新……
2018年6.15更新………………………………………………………… 編譯報錯
Multiple dex files define Lcom/alibaba/android/arouter/routes/ARouter$$Group$$module
複製代碼
通常網上說是依賴版本衝突,其實這個問題是不一樣module之間有相同分組致使的問題,好比a模塊 path = "/message/a",b模塊 path = "/message/b",有相同的message分組,修改爲不同的就能夠了。
最近在用kotlin和java混合開發,發現原有java頁面跳轉新寫的kotlin頁面 arouter 頁面跳轉的時候報異常提示 There is no route match the path……,此時參考官方文檔便可解決,
// 在kotlin的module中添加插件
apply plugin: 'kotlin-kapt'
// 依賴裏 使用kapt 引用
dependencies {
compile 'com.alibaba:arouter-api:x.x.x'
kapt 'com.alibaba:arouter-compiler:x.x.x'
...
}
複製代碼
遇到了一個很蛋疼的問題,在純java寫的module裏經過arouter跳轉到另外一個module裏的kotlin頁面的時候,發現setcontentview方法無效,頁面什麼都不顯示。調試了半天,發現是頁面的xml佈局文件和一個空的xml佈局文件重名了,致使kotlin頁面加載了空頁面的佈局,在此記錄一下,好尷尬。
最後附上完整的demo地址,若是對你有幫助麻煩start鼓勵一下,你的鼓勵是我前進的動力。