Android組件化初探

demo地址:github.com/syg13579/as…android

概述

軟件開發進程也是架構的演進過程,就拿Android來講,從最開始的MVC ,MVP ,MVVP ,再到後來的組件化,插件化,但歸根到底一切的一切,都是爲了項目更好的維護、迭代,下降開發成本。git

在一個項目的開發過程當中,前期咱們可能把全部的功能模塊都放到了一個moudle中,這樣可以快速的開發,但隨着項目壯大,開發人員和功能的增長,就回致使代碼愈來愈臃腫,各個模塊之間的耦合愈來愈重,牽一髮而動全身,這個時候爲了保證項目質量,咱們就須要對項目進行重構。github

咱們能夠根據業務模塊進行查分,把不一樣的業務模塊放到不一樣的moudle中,實現各個業務之間的結構,他們又共同依賴底層公共庫,這就是模塊化的概念,可是當多個模塊中涉及到相同功能時代碼的耦合又會增長,例若有兩個模塊都須要視頻播放的功能,把視頻播放放到兩個組件中就會出現代碼重複的問題,放到公共庫感受也不是很好,這時候就用組件化來解決這個問題bash

模塊化和組件化

模塊化

具體的業務模塊,例如商品詳情模塊,商品發佈模塊 ,搜索模塊架構

組件化

單一的功能組件,如視頻播放組件、分享組件等,每一個組件均可以以一個單獨的 module 開發,而且能夠單獨抽出來做爲 SDK 對外發布使用app

模塊化和組件化的思想是同樣的,都是對代碼進行拆分,但模塊化是按功能模塊進行查分(業務導向),組件化是按功能模塊進行查分(功能導向),模塊化的顆粒度更大一些,組件的顆粒度更小一些,一個項目中模塊和組件同時存在也是很常見的,各自負責各自的事情ide

如上圖所示 是個組件化項目的基本架構模塊化

  • 基礎庫、公共庫:項目所須要的基礎操做類,工具類 ,第三方庫的引入封裝 ,app宿主功能,各個模塊,各個組件都依賴這個庫
  • 組件層:項目用的功能模塊或者業務模塊,如:登陸模塊,視頻播放組件,分享組件等
  • 應用層:宿主工程,APP的主項目,APP入口和主架子

組件化Demo

地址以下: github.com/syg13579/as… 我根據demo項目從如下幾個方面來說解工具

  • 1:項目分析
  • 2:組件application和library動態切換
  • 3:組件間的數據傳遞和方法調用
  • 4:組件類(例如:Fragment)的獲取,以及誇組件頁面跳轉和通信

1:項目分析

如上圖所示,項目的主要結構組件化

  • 應用層:app 項目的主入口
  • 組件層:goods login 商品詳情頁和登陸組件
  • 基礎庫層:assemblebase用來各個組件數據和方法交互的 ,base是經常使用的工具類,各類類庫的封裝

2:組件application和library動態切換

在開發過程當中,爲了可以實現快速開發,組件可以獨立運行就顯的特別重要,moudle通常分爲兩種

  • App 插件,id: com.android.application
  • Library 插件,id: com.android.library

咱們能夠經過配置可動態進行application和library的切換,咱們在各個組件的gradle.properties文件中配置一個控制切換的變量

而後在build.gradle中就能夠經過isRunAlone變量來進行application和library的切換了,主要設計的點有三部分

  • 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'
            }
        }
    }

}
複製代碼

若是以上配置就能夠實現application和library的切換了

3:組件間的數據傳遞和方法調用

因爲主項目、組件之間,組件和組件之間不能直接經過引用進行數據傳遞和方法調用,那麼在開發的過程當中怎麼進行數據傳遞和方法調用呢,能夠經過「接口」+「實現」的方式進行,

assemblebase基礎庫就是用來進行數據傳遞和方法調用的,它被全部組件所依賴,assemblebase提供各個組件對外提供數據和方法調用的抽象service ,同時還有serviceFactory對service進行操做,各個組件在初始化的時候對各自的service進行實現。同時中也會提供全部的 Service 的空實現,以免引發的空指針異常

就以登陸模塊爲例,對外提供兩個數據

public interface ILoginService {

    /**
     * 是否已經登陸
     *
     * @return
     */
    boolean isLogin();

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

}
複製代碼

相關的serviceFactory類以下,能夠經過serviceFactory拉取相關service的實例

public class ServiceFactory {

    private ILoginService loginService;
    private IGoodsService goodsService;

    /**
     * 禁止外部建立 ServiceFactory 對象
     */
    private ServiceFactory() {
    }

    /**
     * 經過靜態內部類方式實現 ServiceFactory 的單例
     */
    public static ServiceFactory getInstance() {
        return Inner.serviceFactory;
    }

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


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

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

複製代碼

在login組件中只須要實現ILoginService,並經過serviceFactory進行設置

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

    @Override
    public String getAccountId() {
        return null;
    }
}
複製代碼

在login的appliction中進行service的設置

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) {

    }
}
複製代碼

可是有這樣一個問題:在集成到app中,LoginApp是沒有被執行的,這個怎麼解決呢,咱們能夠經過反射進行解決

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)的獲取,以及誇組件頁面跳轉和通信

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

public interface IGoodsService {

    /**
     * 建立 GoodsFragment
     * @param bundle
     * @return
     */
    Fragment newGoodsFragment(Bundle bundle);
}
複製代碼

相關組件實現該接口就行

各個組件間頁面的跳轉能夠經過阿里的ARouter實現,我是經過設置ComponentName來實現的,但這種方式好像並無實現真正的代碼隔離

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

總結

經過上面就總體實現了項目組件化,在之後也能夠更多的運用組件化來進行項目開發

相關文章
相關標籤/搜索