Android-組件化開發

參考資料:
http://www.lxweimin.com/p/60c1b9ddd8ab

上一篇我們學習了ARouter,講到ARouter是組件化開發的基礎,那現在讓我們開始組件化開發吧。

1.組件化,模塊化概念

對于組件化的開發,首先要了解模塊化及組件化的概念,這正是是好多小伙伴模糊的,所以我們有必要說明一下。

1.1 組件

組件的英文單詞是component,意思是組件、部件、元件。在App工程上,件是構成業務或者功能模塊的基本單位也就是組件不能被繼續拆分,原則上,組件與組件之間互不依賴。比如我們的圖片上傳功能,可以叫圖片上傳組件,但不能叫圖片上傳模塊,而且組件具有可替換性和重復利用性,可替換性指比如我們的地圖定位組件可以用百度的,也可以用高德的。重復性是指我的地位功能可能在首頁被調用,在其他頁面也要被調用。

1.2 模塊

模塊的英文單詞是Module,由多個組件構成。也就是從粒度上來看,模塊要比組件大,也就是模塊包含組件。舉個例子:以安居客為例,安居客app內有二手房和新房相關功能, 這個二手房和新房就屬于模塊,但這二手房和新房模塊都用到了分享房源的功能,而這個分享功能就屬于組件了。

1.3 組件和模塊的關系

image.png

我們以上圖為例簡單說一下,首頁界面的天貓,聚劃算,餓了么都屬于模塊同時也是一個業務,我們可以叫做業務模塊,而我們繼續點擊天貓進入天貓超市,里面的分享功能,支付所用到的sdk等都算組件。那我們總結一下:模塊化和組件化只不過是我們根據項目的需求,定義不同,一個工程可以由多個模塊組成,每個模塊可以由多個組件構成,模塊可以單獨存在,正是多個模塊構成了整個項目。不論是模塊化還是組件化,它們的目的都是把項目解耦便于代碼的管理。高內聚、低耦合使開發人員分工明確,提高開發效率。

1.4 業務

我們上面說了模塊也可以叫做業務模塊,那么什么是業務呢,我們還是以上圖為例:在淘寶的首頁天貓,聚劃算,充值中心等這些看起來完全不同的業務我們稱之為Business業務。而天貓點擊進入會有搜索業務,預約功能,簽到等功能,聚劃算點擊進入也會有搜索業務,預約功能,簽到等功能,像這種業務我們稱之為基礎業務。我們來張圖說明一下Business業務和基礎業務及組件之間的關系:

image.png

圖中Business業務公用基礎業務,而基礎業務的實現可能依賴于某組件。

1.4 Library

我們剛才說了組件,比如上圖提到的圖片上傳組件,網絡組件,還有分享組件等,那這里要介紹另外一個概念:Library。你比如我們的分享組件用的是友盟分享sdk,我們的網絡組件依賴的是okhttp庫,這些依賴的三方庫都稱之為Library

2.組件化實踐

參考資料:
https://mp.weixin.qq.com/s/-gC8JpmmCZWzcOsH5ZzLtQ
https://mp.weixin.qq.com/s/8_8gGpkpO2QFNkWgSRBwIg
我準備從以下幾個方面來介紹組件化:

  • 代碼解耦
  • 組件或模塊的單獨運行
  • 數據的傳遞與ui跳轉
  • 生命周期管理

2.1代碼解耦

ok,我們先看看我們的demo實現效果,就先簡單上張圖吧,不整gif圖片了:

image.png

你看到的第一眼可能覺得簡單,沒什么大不了,但是這不同于我們平時的實現方法,平時我們的首頁,資訊,我的都是在app中實現,但今天我們的目錄結構是這樣的:


image.png

我先來說一下各個目錄的含義:

  • app
    app模塊是我們的殼工程,平時我們最核心的代碼都要往這里寫,但今天不一樣,app模塊的職能改變了,它最主要的目的是對其他模塊進行整合,確保app能正常運行,里面只有一些簡單的代碼。
  • commonlib
    commonlib 是一個依賴的Library,為什么說她是library呢?因為它的職能是把整個project所用到的library,公用的lib都放到這里,供其它模塊引用,就不需要每個模塊都寫一遍了。以demo為例:


    image.png

注意:采用api代替implementation代替的目的就是為了讓其他模塊能夠引用到

  • module_home,module_zixun,module_user,
    這三個模塊是整個project最核心的地方, 對應我們上面效果圖中的首頁,資訊,我的三個tab。
    這樣做的好處顯而易見 :張三開發首頁,李四開發資訊,王五開發我的 互不影響,提高開發效率 ,這也是組件化的優勢
  • x5webview
    x5webview是作為組件存在的,是我基于騰訊TBS瀏覽服務封裝的(類似webview作用),和分享組件,網路請求組件是一個級別,為了其他模塊的調用。

這里就基本實現了代碼的解耦,你可能有一個疑問?你這一會組件一會模塊是不是有點懵,其實個人認為組件化和模塊化只不過是概念不同,模塊化包含組件化,組件化是模塊化開發中不可缺少的,二者只不過是劃分方式不同,實現方面沒有太大區別。

2.2 組件或模塊的單獨運行

組件或模塊的單獨運行時組件化的又一亮點。還是以我們的demo為例,如果我們想單獨運行module_home模塊改怎么辦?
首先你要知道一點組件變為一個能獨立運行的app要變動那幾個地方?我個人認為一般有三個地方需要變動:

  • 組件或模塊的build.gradle 中的apply plugin: 'com.android.library' 變 apply plugin: 'com.android.application'
  • 組件或模塊的build.gradle 中的defaultConfig配置中的applicationId 根據情況動態變動,如果作為組件或模塊存在則不需要,反之亦然。
  • 功能清單文件的變動,你想如果作為組件或模塊存在我們是不需要下面這些東西的:


    image.png

ok,那我們就依次解決這三個問題:
對于是否要將模塊或組件單獨運行,我們需要定義變量去控制,還是以demo為例,我們在project的gradle.properties文件中定義如下:


image.png

于是第一個問題解決了,我們只需要在模塊的build.gradle中頂部添加如下代碼:


image.png

于是第二個問題也解決了,我們只需要在defaultConfig中增加以下代碼即可:


image.png

這里在提個醒默認情況下我們的applicationId值是我們的包名一致

于是第三個問題也解決了,我們還是在對應的build.gradle文件中的android中增加以下代碼:


image.png

注意這個我們是要配置相關路徑的,如:src/main/runalone/AndroidManifest.xml
那我們在這個位置就有相關文件夾,大家請看:


image.png

里面的內容如下(就是平常的啦):
單獨運行的清單文件:


image.png

作為組件或模塊的清單文件:


image.png

既然實現了組件的單獨運行那么單獨調試也就解決了。

2.3 數據的傳遞與ui跳轉跳轉

ui的跳轉我們主要借助于阿里的Aroute,數據的傳遞可以Aroute和EventBus結合使用,效果更佳。
ARoute的介紹請參考我之前的文章Android-ARouter
還是以項目為例:

  • 跳轉調用其它組件(這里主要是分享組件)
    比如我們要從首頁模塊調用x5組件,那么請看相關代碼:
    首頁相關代碼,再點擊跳轉X5按鈕后:

   @OnClick(R2.id.homemodule_button)
    public void onViewClicked() {
        ARouter.getInstance().build(COMPONENT_X5).withString("url","https://www.baidu.com/").navigation();
    }

x5分享組件相關代碼:


image.png

數據的回調可以結合EvnetBus,這里就不詳細說了。

  • 利用IProvider跨moudle的服務調用,主要用于非Activity,因為我在寫demo中發現在Activity中不好使。直接上代碼了:
    應用場景是我們zhongmodule_zixun模塊中的Fragment要調用X5組件中的X5Test類中需要用到的方法:
  1. 首先在common_lib中定義X5CompService接口繼承IProvider接口,如下:


    image.png

2.x5組件中的X5Test類實現X5CompService接口


image.png

3.在zhongmodule_zixun模塊中的Fragment中獲取X5CompService實例進行調用,進行了簡單的toast


image.png

注意:既然是zhongmodule_zixun模塊中的Fragment中獲取X5CompService的實現類進行調用,那么它就要把X5組件作為依賴:


image.png

2.4 生命周期管理

生命周期的管理我們主要是通過common_lib中的baseApplication(注:其他module中的Application都要繼承baseApplication,確保唯一性)來管理,這里我直接貼出BaseApplication中的所有代碼:

public class BaseApplication extends Application {
    //是否開啟調試
    private  boolean isDebug =true;
    //全局唯一的context
    private static BaseApplication application;
    //Activity管理器
    private ActivityManage activityManage;

    @Override
    public void onCreate() {
        super.onCreate();
        application = this;
        activityManage = new ActivityManage();

        //初始化路由
        initRouter();
    }

    /**
     * 程序終止的時候執行
     */
    @Override
    public void onTerminate() {
        super.onTerminate();
        exitApp();
    }

    /**
     * 退出應用
     */
    public void exitApp() {
        activityManage.finishAll();
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
    }

    /**
     * 初始化路由
     */
    private void initRouter() {
        //必須在初始化之前寫入這兩行
        if (isDebug) {
            //打印日志
            ARouter.openLog();
            //開始調試
            ARouter.openDebug();
        }
        //ARouter的實例化
        ARouter.init(this);
    }


    /**
     * 獲取全局唯一上下文
     *
     * @return BaseApplication
     */
    public static BaseApplication getApplication() {
        return application;
    }


    /**
     * 返回Activity管理器
     */
    public ActivityManage getActivityManage() {
        if (activityManage == null) {
            activityManage = new ActivityManage();
        }
        return activityManage;
    }
}

ActivityManage使我寫的Activity管理工具類,詳情請看相關代碼。

2.5 其他

  • 混淆
    混淆我們是放在各個Module還是app的proguard-rules.pro文件中,答案是app的proguard-rules.pro文件中,因為如果在組件中進行混淆,一旦代碼出現了bug,這個時候就很難根據日志去追蹤bug產生的原因,而且不同組件分別進行混淆非常不方便維護和修改。
  • 使用ButterKnife遇到的問題
    當我將組件單獨運行時是沒有問題的,可如果作為module時就會出現什么需要常量等問題,解決辦法是:


    image.png

    將原本的R.id.homemodule_button改為R2.id.homemodule_button,如果找不到R2,請確保你project的build.gradle中:

image.png

但依賴的butterknife版本是8.4.0:


image.png

每一個用到的模塊中的build.gradle中都需要配置(默認模塊都依賴common_lib,否則單獨添加8.4.0依賴):
apply plugin: 'com.jakewharton.butterknife' 以及


image.png

就這么多吧,詳情請看組件化Demo.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。