Android組件化初探

Android組件化實(shí)踐

demo地址:https://github.com/syg13579/assembleDemo

概述

軟件開發(fā)進(jìn)程也是架構(gòu)的演進(jìn)過程燃辖,就拿Android來說,從最開始的MVC ,MVP ,MVVP ,再到后來的組件化面殖,插件化,但歸根到底一切的一切,都是為了項(xiàng)目更好的維護(hù)、迭代坎吻,降低開發(fā)成本。

在一個(gè)項(xiàng)目的開發(fā)過程中宇葱,前期我們可能把所有的功能模塊都放到了一個(gè)moudle中瘦真,這樣能夠快速的開發(fā)刊头,但隨著項(xiàng)目壯大,開發(fā)人員和功能的增加诸尽,就回導(dǎo)致代碼越來越臃腫原杂,各個(gè)模塊之間的耦合越來越重,牽一發(fā)而動(dòng)全身弦讽,這個(gè)時(shí)候?yàn)榱吮WC項(xiàng)目質(zhì)量污尉,我們就需要對(duì)項(xiàng)目進(jìn)行重構(gòu)。

我們可以根據(jù)業(yè)務(wù)模塊進(jìn)行查分往产,把不同的業(yè)務(wù)模塊放到不同的moudle中被碗,實(shí)現(xiàn)各個(gè)業(yè)務(wù)之間的結(jié)構(gòu),他們又共同依賴底層公共庫仿村,這就是模塊化的概念锐朴,但是當(dāng)多個(gè)模塊中涉及到相同功能時(shí)代碼的耦合又會(huì)增加,例如有兩個(gè)模塊都需要視頻播放的功能蔼囊,把視頻播放放到兩個(gè)組件中就會(huì)出現(xiàn)代碼重復(fù)的問題焚志,放到公共庫感覺也不是很好,這時(shí)候就用組件化來解決這個(gè)問題

模塊化和組件化

模塊化

具體的業(yè)務(wù)模塊畏鼓,例如商品詳情模塊酱酬,商品發(fā)布模塊 ,搜索模塊

組件化

單一的功能組件云矫,如視頻播放組件膳沽、分享組件等,每個(gè)組件都可以以一個(gè)單獨(dú)的 module 開發(fā)让禀,并且可以單獨(dú)抽出來作為 SDK 對(duì)外發(fā)布使用

模塊化和組件化的思想是一樣的挑社,都是對(duì)代碼進(jìn)行拆分,但模塊化是按功能模塊進(jìn)行查分(業(yè)務(wù)導(dǎo)向)巡揍,組件化是按功能模塊進(jìn)行查分(功能導(dǎo)向)痛阻,模塊化的顆粒度更大一些,組件的顆粒度更小一些腮敌,一個(gè)項(xiàng)目中模塊和組件同時(shí)存在也是很常見的阱当,各自負(fù)責(zé)各自的事情

image

如上圖所示 是個(gè)組件化項(xiàng)目的基本架構(gòu)

  • 基礎(chǔ)庫、公共庫:項(xiàng)目所需要的基礎(chǔ)操作類糜工,工具類 斗这,第三方庫的引入封裝 ,app宿主功能啤斗,各個(gè)模塊,各個(gè)組件都依賴這個(gè)庫
  • 組件層:項(xiàng)目用的功能模塊或者業(yè)務(wù)模塊赁咙,如:登錄模塊钮莲,視頻播放組件免钻,分享組件等
  • 應(yīng)用層:宿主工程,APP的主項(xiàng)目崔拥,APP入口和主架子

組件化Demo

地址如下: https://github.com/syg13579/assembleDemo
我根據(jù)demo項(xiàng)目從以下幾個(gè)方面來講解

  • 1:項(xiàng)目分析
  • 2:組件application和library動(dòng)態(tài)切換
  • 3:組件間的數(shù)據(jù)傳遞和方法調(diào)用
  • 4:組件類(例如:Fragment)的獲取,以及夸組件頁面跳轉(zhuǎn)和通訊

1:項(xiàng)目分析

image

如上圖所示极舔,項(xiàng)目的主要結(jié)構(gòu)

  • 應(yīng)用層:app 項(xiàng)目的主入口
  • 組件層:goods login 商品詳情頁和登錄組件
  • 基礎(chǔ)庫層:assemblebase用來各個(gè)組件數(shù)據(jù)和方法交互的 ,base是常用的工具類链瓦,各種類庫的封裝

2:組件application和library動(dòng)態(tài)切換

在開發(fā)過程中拆魏,為了能夠?qū)崿F(xiàn)快速開發(fā),組件能夠獨(dú)立運(yùn)行就顯的特別重要,moudle一般分為兩種

  • App 插件慈俯,id: com.android.application
  • Library 插件渤刃,id: com.android.library

我們可以通過配置可動(dòng)態(tài)進(jìn)行application和library的切換,我們?cè)诟鱾€(gè)組件的gradle.properties文件中配置一個(gè)控制切換的變量

image

然后在build.gradle中就可以通過isRunAlone變量來進(jìn)行application和library的切換了贴膘,主要設(shè)計(jì)的點(diǎn)有三部分

  • plugin屬性的配置
  • applicationId的配置
  • AndroidManifest的配置
if (isRunAlone.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion 26



    defaultConfig {
        if (isRunAlone.toBoolean()) {
            applicationId "ppzh.jd.com.goods"
        }
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            if (isRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

}

如果以上配置就可以實(shí)現(xiàn)application和library的切換了

3:組件間的數(shù)據(jù)傳遞和方法調(diào)用

由于主項(xiàng)目卖子、組件之間,組件和組件之間不能直接通過引用進(jìn)行數(shù)據(jù)傳遞和方法調(diào)用刑峡,那么在開發(fā)的過程中怎么進(jìn)行數(shù)據(jù)傳遞和方法調(diào)用呢洋闽,可以通過「接口」+「實(shí)現(xiàn)」的方式進(jìn)行,

assemblebase基礎(chǔ)庫就是用來進(jìn)行數(shù)據(jù)傳遞和方法調(diào)用的突梦,它被所有組件所依賴诫舅,assemblebase提供各個(gè)組件對(duì)外提供數(shù)據(jù)和方法調(diào)用的抽象service ,同時(shí)還有serviceFactory對(duì)service進(jìn)行操作,各個(gè)組件在初始化的時(shí)候?qū)Ω髯缘膕ervice進(jìn)行實(shí)現(xiàn)宫患。同時(shí)中也會(huì)提供所有的 Service 的空實(shí)現(xiàn)刊懈,以避免引起的空指針異常

就以登錄模塊為例,對(duì)外提供兩個(gè)數(shù)據(jù)

public interface ILoginService {

    /**
     * 是否已經(jīng)登錄
     *
     * @return
     */
    boolean isLogin();

    /**
     * 獲取登錄用戶的 AccountId
     *
     * @return
     */
    String getAccountId();

}

相關(guān)的serviceFactory類如下撮奏,可以通過serviceFactory拉取相關(guān)service的實(shí)例

public class ServiceFactory {

    private ILoginService loginService;
    private IGoodsService goodsService;

    /**
     * 禁止外部創(chuàng)建 ServiceFactory 對(duì)象
     */
    private ServiceFactory() {
    }

    /**
     * 通過靜態(tài)內(nèi)部類方式實(shí)現(xiàn) ServiceFactory 的單例
     */
    public static ServiceFactory getInstance() {
        return Inner.serviceFactory;
    }

    private static class Inner {
        private static ServiceFactory serviceFactory = new ServiceFactory();
    }


//    ------------------------LoginService------------------------
    /**
     * 接收 Login 組件實(shí)現(xiàn)的 Service 實(shí)例
     */
    public void setLoginService(ILoginService loginService) {
        this.loginService = loginService;
    }

    /**
     * 返回 Login 組件的 Service 實(shí)例
     */
    public ILoginService getLoginService() {
        if (loginService == null) {
            loginService = new EmptyLoginService();
        }
        return loginService;
    }

在login組件中只需要實(shí)現(xiàn)ILoginService俏讹,并通過serviceFactory進(jìn)行設(shè)置

public class LoginService implements ILoginService {
    @Override
    public boolean isLogin() {
        return false;
    }

    @Override
    public String getAccountId() {
        return null;
    }
}

在login的appliction中進(jìn)行service的設(shè)置

public class LoginApp extends BaseApp {

    @Override
    public void onCreate() {
        super.onCreate();
        initModuleApp(this);
        initModuleData(this);
    }

    @Override
    public void initModuleApp(Application application) {
        ServiceFactory.getInstance().setLoginService(new LoginService());
    }

    @Override
    public void initModuleData(Application application) {

    }
}

但是有這樣一個(gè)問題:在集成到app中,LoginApp是沒有被執(zhí)行的畜吊,這個(gè)怎么解決呢泽疆,我們可以通過反射進(jìn)行解決

public class AssembleApplication extends BaseApp {
    @Override
    public void onCreate() {
        super.onCreate();
        initModuleApp(this);
        initModuleData(this);
        initComponentList();
    }

    @Override
    public void initModuleApp(Application application) {

    }

    @Override
    public void initModuleData(Application application) {

    }

    //初始化組件
    //通過反射初始化
    private void initComponentList(){
        for (String moduleApp : AppConfig.moduleApps) {
            try {
                Class clazz = Class.forName(moduleApp);
                BaseApp baseApp = (BaseApp) clazz.newInstance();
                baseApp.initModuleApp(this);
                baseApp.initModuleData(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}

如上所示就完成了

4:組件類(例如:Fragment)的獲取,以及夸組件頁面跳轉(zhuǎn)和通訊

fragment的獲取也是通過service來完成的

public interface IGoodsService {

    /**
     * 創(chuàng)建 GoodsFragment
     * @param bundle
     * @return
     */
    Fragment newGoodsFragment(Bundle bundle);
}

相關(guān)組件實(shí)現(xiàn)該接口就行

各個(gè)組件間頁面的跳轉(zhuǎn)可以通過阿里的ARouter實(shí)現(xiàn),我是通過設(shè)置ComponentName來實(shí)現(xiàn)的玲献,但這種方式好像并沒有實(shí)現(xiàn)真正的代碼隔離

 /**
     *
     * 去登陸
     *
     * 跨組件頁面跳轉(zhuǎn)
     */
    private void toLogin(){
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(mContext, "ppzh.jd.com.login.LoginActivity"));
        startActivityForResult(intent,LOGIN_REQUEST_CODE);
    }

總結(jié)

通過上面就整體實(shí)現(xiàn)了項(xiàng)目組件化殉疼,在以后也可以更多的運(yùn)用組件化來進(jìn)行項(xiàng)目開發(fā)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市捌年,隨后出現(xiàn)的幾起案子瓢娜,更是在濱河造成了極大的恐慌,老刑警劉巖礼预,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眠砾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡托酸,警方通過查閱死者的電腦和手機(jī)褒颈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門柒巫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谷丸,你說我怎么就攤上這事堡掏。” “怎么了刨疼?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵泉唁,是天一觀的道長。 經(jīng)常有香客問我揩慕,道長亭畜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任漩绵,我火速辦了婚禮贱案,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘止吐。我一直安慰自己宝踪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布碍扔。 她就那樣靜靜地躺著瘩燥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪不同。 梳的紋絲不亂的頭發(fā)上厉膀,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音二拐,去河邊找鬼服鹅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛百新,可吹牛的內(nèi)容都是我干的企软。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饭望,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼仗哨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铅辞,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤厌漂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后斟珊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苇倡,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雏节。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胜嗓。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钩乍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情怔锌,我是刑警寧澤寥粹,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站埃元,受9級(jí)特大地震影響涝涤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜岛杀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一阔拳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类嗤,春花似錦糊肠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至精偿,卻和暖如春弧圆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笔咽。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工搔预, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叶组。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓拯田,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扶叉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勿锅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容