demo地址:github.com/syg13579/as…android
軟件開發進程也是架構的演進過程,就拿Android來講,從最開始的MVC ,MVP ,MVVP ,再到後來的組件化,插件化,但歸根到底一切的一切,都是爲了項目更好的維護、迭代,下降開發成本。git
在一個項目的開發過程當中,前期咱們可能把全部的功能模塊都放到了一個moudle中,這樣可以快速的開發,但隨着項目壯大,開發人員和功能的增長,就回致使代碼愈來愈臃腫,各個模塊之間的耦合愈來愈重,牽一髮而動全身,這個時候爲了保證項目質量,咱們就須要對項目進行重構。github
咱們能夠根據業務模塊進行查分,把不一樣的業務模塊放到不一樣的moudle中,實現各個業務之間的結構,他們又共同依賴底層公共庫,這就是模塊化的概念,可是當多個模塊中涉及到相同功能時代碼的耦合又會增長,例若有兩個模塊都須要視頻播放的功能,把視頻播放放到兩個組件中就會出現代碼重複的問題,放到公共庫感受也不是很好,這時候就用組件化來解決這個問題bash
具體的業務模塊,例如商品詳情模塊,商品發佈模塊 ,搜索模塊架構
單一的功能組件,如視頻播放組件、分享組件等,每一個組件均可以以一個單獨的 module 開發,而且能夠單獨抽出來做爲 SDK 對外發布使用app
模塊化和組件化的思想是同樣的,都是對代碼進行拆分,但模塊化是按功能模塊進行查分(業務導向),組件化是按功能模塊進行查分(功能導向),模塊化的顆粒度更大一些,組件的顆粒度更小一些,一個項目中模塊和組件同時存在也是很常見的,各自負責各自的事情ide
如上圖所示 是個組件化項目的基本架構模塊化
地址以下: github.com/syg13579/as… 我根據demo項目從如下幾個方面來說解工具
如上圖所示,項目的主要結構組件化
在開發過程當中,爲了可以實現快速開發,組件可以獨立運行就顯的特別重要,moudle通常分爲兩種
咱們能夠經過配置可動態進行application和library的切換,咱們在各個組件的gradle.properties文件中配置一個控制切換的變量
而後在build.gradle中就能夠經過isRunAlone變量來進行application和library的切換了,主要設計的點有三部分
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的切換了
因爲主項目、組件之間,組件和組件之間不能直接經過引用進行數據傳遞和方法調用,那麼在開發的過程當中怎麼進行數據傳遞和方法調用呢,能夠經過「接口」+「實現」的方式進行,
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();
}
}
}
}
複製代碼
如上所示就完成了
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);
}
複製代碼
經過上面就總體實現了項目組件化,在之後也能夠更多的運用組件化來進行項目開發