概述
軟體開發程序也是架構的演進過程,就拿Android來說,從最開始的MVC ,MVP ,MVVP ,再到後來的元件化,插件化,但歸根到底一切的一切,都是為了項目更好的維護、疊代,降低開發成本。
在一個項目的開發過程中,前期我們可能把所有的功能子產品都放到了一個moudle中,這樣能夠快速的開發,但随着項目壯大,開發人員和功能的增加,就回導緻代碼越來越臃腫,各個子產品之間的耦合越來越重,牽一發而動全身,這個時候為了保證項目品質,我們就需要對項目進行重構。
我們可以根據業務子產品進行查分,把不同的業務子產品放到不同的moudle中,實作各個業務之間的結構,他們又共同依賴底層公共庫,這就是子產品化的概念,但是當多個子產品中涉及到相同功能時代碼的耦合又會增加,例如有兩個子產品都需要視訊播放的功能,把視訊播放放到兩個元件中就會出現代碼重複的問題,放到公共庫感覺也不是很好,這時候就用元件化來解決這個問題
子產品化群組件化
子產品化
具體的業務子產品,例如商品詳情子產品,商品釋出子產品 ,搜尋子產品
元件化
單一的功能元件,如視訊播放元件、分享元件等,每個元件都可以以一個單獨的 module 開發,并且可以單獨抽出來作為 SDK 對外釋出使用
子產品化群組件化的思想是一樣的,都是對代碼進行拆分,但子產品化是按功能子產品進行查分(業務導向),元件化是按功能子產品進行查分(功能導向),子產品化的顆粒度更大一些,元件的顆粒度更小一些,一個項目中子產品群組件同時存在也是很常見的,各自負責各自的事情
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yY2EDNzATOkVDMkFWNmVWOzEmMlFTO4kTZmNGM5MjY28CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
如上圖所示 是個元件化項目的基本架構
- 基礎庫、公共庫:項目所需要的基礎操作類,工具類 ,第三方庫的引入封裝 ,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;
}
}
[圖檔上傳失敗…(image-4066c0-1627130966768)]
在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);
}
總結
通過上面就整體實作了項目元件化,在以後也可以更多的運用元件化來進行項目開發
最後
小編學習提升時,順帶從網上收集整理了一些 Android 開發相關的學習文檔、面試題、Android 核心筆記等等文檔,希望能幫助到大家學習提升,如有需要參考的可以直接去我 CodeChina位址:https://codechina.csdn.net/u012165769/Android-T3 通路查閱。