Android元件化入門:一步步搭建元件化架構

胖宅老鼠發表於2019-04-30

1、前言

最近因為業務需求變更,有考慮採用元件化架構進行開發,這方面我之前沒有接觸過。關於元件化的文章很多,各方大神更是提出了各種的元件化方案,我也看了很多相關文章。但是學習新東西看的再多,不如動手做一次,先不考慮複雜的東西,先動手做個簡單的Demo更有助於理解元件化的思想。元件化相關理論知識這裡就不多講了,想要了解的可以自己去搜或者去看Android元件化方案這篇文章。廢話不多說,直接動手開碼。

2、搭建元件化Demo

先開啟Android Studio新建一個專案。

Android元件化入門:一步步搭建元件化架構

步驟一: 新建config.gradle,統一管理build.gradle中的相關內容

然後在專案目錄下新建一個config.gradle檔案。

Android元件化入門:一步步搭建元件化架構
接著在這個檔案內新增如下程式碼:

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和三方庫等進行統一管理。

接著修改app下的build.gradle裡設定內容

將原來的compileSdkVersionapplicationIdminSdkVersionversionCode和三方庫等替換成對應config.gradle中定義的值。

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"
複製程式碼

Android元件化入門:一步步搭建元件化架構
然後點選Sync Now同步。最後在進行下一步前,新建一個MyApplication,在AndroidManifest設定name屬性。

步驟二:建立Main模組,搬空app殼工程

我們知道元件化中需要一個app殼工程,這個殼工程中不處理任何業務,就只是一個空殼,由它將所需要的各個元件模組組合起來,構成一個完整的應用。而現在專案中的app還是存在預設的入口Activity的,所以要新建一個ModuleMain將預設的MainActivity和其佈局檔案搬過去。

Android元件化入門:一步步搭建元件化架構

接著進入app的AndroidManifest檔案將註冊Activity的相關程式碼也搬到ModuleMain模組的AndroidManifest中去,只留下application標籤。

這裡注意元件化專案中每個Module都會有自己的AndroidManifest檔案,最後打包時會將這些檔案合併成一個檔案,所以會出現application標籤中的屬性重複問題,要在app的AndroidManifest檔案中新增如下兩行程式碼:

 xmlns:tools="http://schemas.android.com/tools" 
複製程式碼
 tools:replace="android:name,android:label,android:icon, android:theme,android:allowBackup"
複製程式碼

這裡的namelabeliconthemeallowBackup都可能會有重複,所以全部寫上之間用逗號隔開。完整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裡來。

Android元件化入門:一步步搭建元件化架構

步驟三 :新建ApplicationAndroidManifest檔案

在app殼工程和ModuleMain中分別新建一個Application,因為Moudule也是需要可以單獨執行的。

Android元件化入門:一步步搭建元件化架構
接著在ModuleMain裡新建AndroidManifest檔案,因為作為ModuleApplication是會有不一樣的所以要做區分,在main目錄下新建module資料夾和application資料夾分別存放兩個情況下的AndroidManifest檔案,將原來的AndroidManifest檔案拖到module下,再拷貝一份到application下。拷貝完了記得在兩個AndroidManifestapplication標籤下設定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的值,進行ApplicationModule兩種模式的切換,將ModuleMain作為app 的模組執行或者是單獨作為一個應用執行了。

Android元件化入門:一步步搭建元件化架構

步驟四:新建其他元件Module和解決資原始檔衝突

接著按照新建ModuleMain的步驟重複新建其他業務Module,這裡我新建了3個Module,業務A:ModuleA與業務B:ModuleB和一個BaseModule。其中BaseModule主要存放一些基礎類和工具類,只做為Module為上層業務模組提供服務。

Android元件化入門:一步步搭建元件化架構
接下來解決資原始檔衝突的問題,進入ModuleMain的build.gradle新增下面這行程式碼,為資原始檔命名規範一個統一開頭:

resourcePrefix "modulemain_"
複製程式碼

新增後起名是沒按照規範Android Studio就會有一個提示:

Android元件化入門:一步步搭建元件化架構
按要求修改檔名後提示消失。
Android元件化入門:一步步搭建元件化架構

步驟五:使用ARouter進行元件間通訊

接下來就要處理元件間的通訊問題,採用阿里的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);
    }
}
複製程式碼

執行效果:

Android元件化入門:一步步搭建元件化架構
Android元件化入門:一步步搭建元件化架構
這裡看到模組之間介面通訊正常,並且單個業務模組也可以單獨執行,這樣一個最基本的元件化架構Demo差不多就完成了。

3、將Module作為遠端maven倉庫

在開發中,可能會把一些公用Module傳到私有伺服器上,然後在專案中直接依賴使用。下面就將Module上傳到Github作為遠端maven倉庫,在專案直接引用。首先新建一個專案,建立一個UtilModule。

Android元件化入門:一步步搭建元件化架構

將原來專案中的工具類移到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

Android元件化入門:一步步搭建元件化架構

進入設定的目錄檢視,aar已經打包好了。

Android元件化入門:一步步搭建元件化架構

接著開啟Github建立一個新倉庫:

Android元件化入門:一步步搭建元件化架構

按照Github上的命令,將本地打包好的UtilModule上傳到Github上:

Android元件化入門:一步步搭建元件化架構
Android元件化入門:一步步搭建元件化架構
Android元件化入門:一步步搭建元件化架構
上傳完成後將倉庫地址複製下來,將其中的github.com部分修改為raw.githubusercontent.com再在結尾加上/master表示是主分支,新增到專案中的build.gradle中。

Android元件化入門:一步步搭建元件化架構
接著在到Module的build.gradle中新增依賴:

utilmodule      : 'com.example.utilmodule:utilmodule:' + UTIL_MODULE_VERSION
複製程式碼
implementation rootProject.ext.dependencies.utilmodule
複製程式碼

這裡就是之前設定的包名:Module名:版本號。SyncNow之後刪除原來專案中的工具類,然後在程式碼裡使用遠端倉庫的工具類測試:

Android元件化入門:一步步搭建元件化架構
執行列印日誌:

D/com.example.modulemain.MainActivity: onCreate:false
複製程式碼

這說明遠端倉庫依賴成功已經能正常使用其中的類和方法。

4、總結

這篇文章主要是記錄下我初識元件化,搭建元件化Demo的過程,Demo主要對於我對元件化思想的理解和體驗還是很有幫助的,Demo中還有很多沒考慮到的地方,比如Application的動態配置合併、Fragment、元件化的混淆等等,也是我正在學習的問題。這篇文章主要供和我一樣對元件化這塊不太瞭解的新手做參考,希望能對新手有所幫助。

最後再貼上幾個總結得比較好的開源元件化方案的連結,也是我後面準備研究學習的方案:
多個維度對比一些有代表性的開源android元件化開發方案
AppJoint方案
ArmsComponent方案

補一下文中Demo原始碼地址:github.com/syDeveloper…

相關文章