0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

剖析Android項(xiàng)目組件化重構(gòu)架構(gòu)

jf_78858299 ? 來源:小余的自習(xí)室 ? 作者:小余的自習(xí)室 ? 2023-03-30 10:41 ? 次閱讀

1.組件化重構(gòu)效果

這里先看下我們重構(gòu)前后的框架圖比較:

重構(gòu)前:

圖片

傳統(tǒng)代碼架構(gòu).png

重構(gòu)后

圖片

組件化代碼架構(gòu).png

  • ft_xxx表示業(yè)務(wù)層模塊 lib_xxx表示基礎(chǔ)庫模塊

重構(gòu)后的架構(gòu)圖如下

圖片

服務(wù)接口調(diào)用.png

重構(gòu)前的代碼業(yè)務(wù)封裝在宿主app中,業(yè)務(wù)耦合嚴(yán)重,如果修改一個(gè)業(yè)務(wù)模塊,需要對整個(gè)app進(jìn)行完整測試,測試工作量巨大

重構(gòu)后,我們只需要對單一app進(jìn)行獨(dú)立調(diào)試即可。

重構(gòu)后的框架結(jié)構(gòu):所有的業(yè)務(wù)組件之間通訊都通過ft_base_service進(jìn)行通訊

2.組件化重構(gòu)準(zhǔn)則

  • 1.單一業(yè)務(wù)可以單獨(dú)調(diào)試,也可以作為lib提供給宿主app使用
  • 2.同一級別的模塊不允許直接調(diào)用,比如我們的ft_home組件不允許直接調(diào)用ft_login組件,不然組件化的意義就不存在了
  • 3.組件間通訊不能直接使用顯示的class文件跳轉(zhuǎn),可以考慮很用ARouter框架進(jìn)行解耦
  • 4.每個(gè)組件可打包為aar或者jar上傳到maven私服,宿主使用的時(shí)候,直接引用私服中aar包即可

能做到以上幾點(diǎn),你的app就可以稱為一個(gè)組件化框架的app了。

3.組件化重構(gòu)思路

圖片

重構(gòu)思路.png

    1. :拆代碼,拆資源,拆構(gòu)建

      由于所有業(yè)務(wù)和資源都耦合在宿主app中,所以需要將代碼和資源拆開到對應(yīng)模塊中

      當(dāng)然我們的構(gòu)建build.gradle也需要拆分到不同模塊中

    1. :對外提供接口

      組件化之間不能直接通訊,需要使用暴露接口的方式對外通訊

    1. :反復(fù)測試

      重構(gòu)后代碼,需要反復(fù)測試,防止出現(xiàn)意想不到的bug

4.組件化重構(gòu)過程

這里我以登錄業(yè)務(wù)ft_login為例子:

1. 步驟1 :首先新建一個(gè)業(yè)務(wù)模塊ft_login,然后在宿主app中將登錄功能相關(guān)聯(lián)的代碼和資源抽離到ft_login

2. 步驟2 :將和登錄構(gòu)建相關(guān)的依賴分配到ft_login構(gòu)建中。

3. 步驟3 :單獨(dú)調(diào)試功能實(shí)現(xiàn)

  • 3.1:在gradle.properties中創(chuàng)建一個(gè)全局變量:isRunAlone=true
  • 3.2:在build.gradle中:
if(isRunAlone.toBoolean()){
    apply plugin:'com.android.application'
}else{
    apply plugin:'com.android.library'
}

android {
    compileSdkVersion 33
    buildToolsVersion "33.0.0"

    defaultConfig {
        if(isRunAlone.toBoolean()){
            applicationId 'com.anna.ft_login'
        }
        ...
    }
    sourceSets {
        main {
            java {
                srcDirs = ['src/main/java']
            }
            resources {
                srcDirs = ['src/main/res']
            }
            aidl {
                srcDirs = ['src/main/aidl']
            }
            manifest {
                if(isRunAlone.toBoolean()){
                    srcFile 'src/main/manifest/AndroidManifest.xml'
                }else {
                    srcFile 'src/main/AndroidManifest.xml'
                }
            }
        }
    }
}
def dependList = [rootProject.depsLibs.okhttp,
                  rootProject.depsLibs.gson,
                  rootProject.depsLibs.appcompact,
                  rootProject.depsLibs.design,
                  rootProject.depsLibs.eventbus,
                  rootProject.depsLibs.arouterapi,
                  ':lib_network',':lib_common_ui',':ft_base_service']


dependencies {
    if(!isRunAlone.toBoolean()){
        dependList.each { String depend ->
            depend.startsWithAny(':lib',':ft')? compileOnly(project(depend)):compileOnly(depend){
                switch (depend){
                    case rootProject.depsLibs.arouterapi:
                        exclude group: 'com.android.support'
                        break;
                }
            }
        }
    }else {
        dependList.each { String depend ->
            depend.startsWithAny(':lib',':ft')? implementation(project(depend)):implementation(depend) {
                switch (depend) {
                    case rootProject.depsLibs.arouterapi:
                        exclude group: 'com.android.support'
                        break;
                }
            }
        }
    }
    //arouter注解處理器
    annotationProcessor rootProject.depsLibs.aroutercompiler

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

}

單獨(dú)調(diào)試狀態(tài)下注意四點(diǎn)

  • 1.引用application插件
  • 2.引入applicationId
  • 3.引入不同給的sourceSets構(gòu)建路徑
  • 4.引入的庫單獨(dú)調(diào)試狀態(tài)下需要使用implementation導(dǎo)入,不能使用compileOnly

實(shí)現(xiàn)上面四點(diǎn), 只要打開isRunAlone就可作為一個(gè)單獨(dú)app運(yùn)行了 。

4.步驟4:組件間通訊

這里,我們引入一個(gè)ft_base_service模塊,這個(gè)模塊用來實(shí)現(xiàn)組件間通訊用,需要調(diào)用別的業(yè)務(wù)模塊都需要使用這個(gè)模塊才能通訊、

業(yè)務(wù)模塊與ft_base_service之間通訊使用的是路由ARouter

關(guān)于ARouter的使用可以參考這篇文章:

Android開源系列-組件化框架Arouter-(一)使用方式詳解

  • 1.創(chuàng)建ft_base_service,在這個(gè)模塊中:創(chuàng)建一個(gè)LoginService接口繼承IProvider

引入ARouter依賴

android {
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
        }
    }
}
//arouter核心api
implementation rootProject.depsLibs.arouterapi
//arouter注解處理器
annotationProcessor rootProject.depsLibs.aroutercompiler

創(chuàng)建LoginService:

public interface LoginService extends IProvider {
    boolean hasLogin();
    void login(Context context);
}
  • 2.在ft_login業(yè)務(wù)模塊中實(shí)現(xiàn)LoginService接口

注意這里因?yàn)槭褂昧薃Router注解,所以也需要引入ARouter依賴

@Route(path = "/login/login_service")
public class LoginServiceImpl implements LoginService {
    Context context;
    @Override
    public boolean hasLogin() {
        return UserManager.getInstance().hasLogined();
    }

    @Override
    public void login(Context context) {
        LoginActivity.start(context);
    }

    @Override
    public void init(Context context) {
        Log.d("TAG","LoginServiceImpl is init");
    }
}
  • 3.在ft_base_service模塊中對LoginService接口進(jìn)行依賴注入
public class LoginImpl {

    @Autowired(name = "/login/login_service")
    public LoginService mLoginService;
    private static LoginImpl mLoginImpl = null;
    public static LoginImpl getInstance() {
        if (mLoginImpl == null) {
            synchronized (LoginImpl.class) {
                if (mLoginImpl == null) {
                    mLoginImpl = new LoginImpl();
                }
                return mLoginImpl;
            }
        }
        return mLoginImpl;
    }

    private LoginImpl(){
        ARouter.getInstance().inject(this);
    }
    public boolean hasLogin(){
        return mLoginService.hasLogin();
    }
    public void login(Context context){
        mLoginService.login(context);
    }

}

筆者使用了一個(gè) 單例類LoginImpl ,在構(gòu)造器中對LoginService依賴注入

ARouter.getInstance().inject(this);

然后宿主app或者其他模塊引用登錄業(yè)務(wù)功能時(shí),需要依賴ft_base_service模塊,并使用LoginImpl的接口即可。

這里要說明下,平時(shí)我們使用的四大組件跳轉(zhuǎn)也可以使用這個(gè)方式來處理,在服務(wù)接口中定義跳轉(zhuǎn)接口即可。當(dāng)然也可以使用Arouter的Activity跳轉(zhuǎn)方式或者Fragment實(shí)例獲取方式

  • 5.代碼打包aar上傳到maven私服

關(guān)于這塊maven私服更多內(nèi)容可以參考這篇文章:

Gradle筑基篇(六)-使用Maven實(shí)現(xiàn)組件化類庫發(fā)布

這里我們封裝了一個(gè)通用組件發(fā)布庫:

apply plugin: 'maven'


uploadArchives {
    repositories {
        mavenDeployer {
            // 是否快照版本
            def isSnapShot = Boolean.valueOf(MAVEN_IS_SNAPSHOT)
            def versionName = MAVEN_VERSION
            if (isSnapShot) {
                versionName += "-SNAPSHOT"
            }
            // 組件信息
            pom.groupId = MAVEN_GROUP_ID
            pom.artifactId = MAVEN_ARTIFACTID
            pom.version = versionName

            // 快照倉庫路徑
            snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) {
                authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
            }
            // 發(fā)布倉庫路徑
            repository(url: uri(MAVEN_RELEASE_URL)) {
                authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
            }

            println("###################################"
                    + "\\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging
                    + "\\nrepository =" + (isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL)
                    + "\\n###################################"
            )
        }
    }
}

然后在對應(yīng)的組件下面引用:

apply from:file('../maven.gradle')

發(fā)布的時(shí)候直接在Gradle面板中點(diǎn)擊uploadArchives任務(wù)即可

圖片

task面板.png

經(jīng)過上面幾個(gè)步驟就基本完成了login組件的封裝并發(fā)布,且對外提供了login組件接口 其他組件也是按照上面的邏輯進(jìn)行重構(gòu)

更多詳細(xì)信息可以自己拿到項(xiàng)目源代碼查看。

5.組件化重構(gòu)總結(jié)

組件化不僅是一種架構(gòu),更是一種思想,架構(gòu)是可以變得,但是核心思想?yún)s是統(tǒng)一的,在拆分代碼的時(shí)候,要注意模塊的顆粒度,不是顆粒度越小就越好,模塊分離的好,后期對組件改造會(huì)有很大幫助, 關(guān)于組件化的文章就講到這里,組件化重構(gòu)的項(xiàng)目已經(jīng)上傳到Github。后面會(huì)出一期插件化的項(xiàng)目改造。敬請期待。

** demo地址: https://github.com/ByteYuhb/anna_music_app **

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3909

    瀏覽量

    126933
  • APP
    APP
    +關(guān)注

    關(guān)注

    33

    文章

    1563

    瀏覽量

    72278
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4700

    瀏覽量

    68111
收藏 人收藏

    評論

    相關(guān)推薦

    Android的系統(tǒng)架構(gòu)包括哪些部分?

    什么是Android以后,再來說說Android的系統(tǒng)架構(gòu)包括哪些部分。Android的系統(tǒng)架構(gòu)和其操作系統(tǒng)一樣,采用了分層的
    發(fā)表于 12-04 14:11

    【懸賞100塊】如何實(shí)現(xiàn)FPGA可重構(gòu)計(jì)算(Android平臺(tái))

    LZ我是大四計(jì)算機(jī)的,沒錯(cuò),我在做畢設(shè),而且?guī)缀跻换I莫展。題目是在Android平臺(tái)上實(shí)現(xiàn)可重構(gòu)計(jì)算:簡單說,就是實(shí)現(xiàn)應(yīng)用程序把一部分計(jì)算密集型的任務(wù)交給FPGA來計(jì)算,把FPGA作為CPU的一個(gè)
    發(fā)表于 05-20 20:03

    STM32庫函數(shù)架構(gòu)剖析

    STM32庫函數(shù)架構(gòu)剖析
    發(fā)表于 06-20 17:08

    [資料分享]+Android內(nèi)核剖析

    `[資料分享]+Android內(nèi)核剖析一、看威武霸氣的封面作者:柯元旦 二、讀讀簡介,看看適合你嗎? 本書詳細(xì)分析了Android內(nèi)核的內(nèi)部機(jī)制,包括窗口管理系統(tǒng)、Activity管理系統(tǒng)
    發(fā)表于 09-26 10:46

    Android手機(jī)操控ARM開發(fā)板外圍硬件設(shè)備【創(chuàng)科之龍】原創(chuàng)

    _源碼架構(gòu)分布剖析零死角玩轉(zhuǎn)Android下_如何進(jìn)行Android源碼編譯Makefile剖析 零死角玩轉(zhuǎn)
    發(fā)表于 01-12 22:31

    Android RIL架構(gòu)簡介

    架構(gòu)說起—Android架構(gòu)從手機(jī)的架構(gòu)說起—Android給客戶的價(jià)值從RIL的架構(gòu)說起—R
    發(fā)表于 09-25 15:46

    Android系統(tǒng)的CPU架構(gòu)

    Android CPU 架構(gòu)詳解
    發(fā)表于 04-15 12:00

    初探Android系統(tǒng)整體架構(gòu)

    Android系統(tǒng)龐大且錯(cuò)綜復(fù)雜,今天小編將帶領(lǐng)大家初探Android系統(tǒng)整體架構(gòu),一窺其全貌。引言本文作為Android系統(tǒng)架構(gòu)的開篇,起
    發(fā)表于 08-20 06:32

    XML在可重構(gòu)制造執(zhí)行系統(tǒng)組件管理中的應(yīng)用

    基于組件技術(shù)研究和開發(fā)可重構(gòu)制造執(zhí)行系統(tǒng)(RMES),需要解決大量不同功能業(yè)務(wù)組件的維護(hù)問題。本文基于XML-Schema 機(jī)制設(shè)計(jì)了一套標(biāo)準(zhǔn)描述方法,來定義RMES
    發(fā)表于 08-24 11:31 ?7次下載

    深入剖析Android消息機(jī)制

    深入剖析Android消息機(jī)制
    發(fā)表于 01-22 21:11 ?11次下載

    關(guān)于iOS組件的分析

    1.組件的目的是什么? 最近一兩年很多人都想在項(xiàng)目里面搞組件,覺得搞組件
    發(fā)表于 09-25 10:28 ?0次下載
    關(guān)于iOS<b class='flag-5'>組件</b><b class='flag-5'>化</b>的分析

    華為BBF CloudCO架構(gòu)標(biāo)準(zhǔn)為運(yùn)營商重構(gòu)寬帶網(wǎng)絡(luò)架構(gòu)提供了關(guān)鍵的參考

    BBF CloudCO架構(gòu)標(biāo)準(zhǔn)為運(yùn)營商重構(gòu)寬帶網(wǎng)絡(luò)架構(gòu)提供了關(guān)鍵的標(biāo)準(zhǔn)參考,是邁向云網(wǎng)絡(luò)的關(guān)鍵一步。
    發(fā)表于 10-25 09:45 ?2772次閱讀

    重構(gòu)計(jì)算架構(gòu)將引領(lǐng)未來芯片的市場發(fā)展

    AI芯片創(chuàng)業(yè)公司清微智能首款可重構(gòu)計(jì)算架構(gòu)AI芯片實(shí)現(xiàn)量產(chǎn)的消息在業(yè)內(nèi)迅速傳開,可重構(gòu)計(jì)算架構(gòu)芯片再次引發(fā)一波討論的熱潮。
    的頭像 發(fā)表于 12-16 15:55 ?3179次閱讀

    重構(gòu)架構(gòu)技術(shù)的快速發(fā)展,未來將應(yīng)用于太空

    如今,技術(shù)的進(jìn)步使得低軌飛行越來越容易商用。賽靈思太空產(chǎn)品系統(tǒng)架構(gòu)師Minal Sawant表示,航天行業(yè)的主要挑戰(zhàn)之一是用于各種項(xiàng)目的體系結(jié)構(gòu),多任務(wù)的靈活架構(gòu)平臺(tái)是如今的一個(gè)挑戰(zhàn)
    發(fā)表于 05-22 10:35 ?549次閱讀

    基于AndroidCrop進(jìn)行鴻蒙的開源圖片裁剪組件

    項(xiàng)目是基于開源項(xiàng)目 AndroidCrop 進(jìn)行鴻蒙的移植和開發(fā)的,可以通過項(xiàng)目標(biāo)簽以及github地址( https://github
    發(fā)表于 03-23 09:53 ?2次下載