1.組件化重構(gòu)效果
這里先看下我們重構(gòu)前后的框架圖比較:
重構(gòu)前:
傳統(tǒng)代碼架構(gòu).png
重構(gòu)后
組件化代碼架構(gòu).png
ft_xxx
表示業(yè)務(wù)層模塊lib_xxx
表示基礎(chǔ)庫模塊
重構(gòu)后的架構(gòu)圖如下 :
服務(wù)接口調(diào)用.png
重構(gòu)前的代碼業(yè)務(wù)封裝在宿主app中,業(yè)務(wù)耦合嚴(yán)重,如果修改一個業(yè)務(wù)模塊,需要對整個app進(jìn)行完整測試,測試工作量巨大
而重構(gòu)后,我們只需要對單一app進(jìn)行獨立調(diào)試即可。
重構(gòu)后的框架結(jié)構(gòu):所有的業(yè)務(wù)組件之間通訊都通過ft_base_service
進(jìn)行通訊
2.組件化重構(gòu)準(zhǔn)則
- 1.單一業(yè)務(wù)可以單獨調(diào)試,也可以作為lib提供給宿主app使用
- 2.同一級別的模塊不允許直接調(diào)用,比如我們的ft_home組件不允許直接調(diào)用ft_login組件,不然組件化的意義就不存在了
- 3.組件間通訊不能直接使用顯示的class文件跳轉(zhuǎn),可以考慮很用ARouter框架進(jìn)行解耦
- 4.每個組件可打包為aar或者jar上傳到maven私服,宿主使用的時候,直接引用私服中aar包即可
能做到以上幾點,你的app就可以稱為一個組件化框架的app了。
3.組件化重構(gòu)思路
重構(gòu)思路.png
-
-
拆 :拆代碼,拆資源,拆構(gòu)建
由于所有業(yè)務(wù)和資源都耦合在宿主app中,所以需要將代碼和資源拆開到對應(yīng)模塊中
當(dāng)然我們的構(gòu)建build.gradle也需要拆分到不同模塊中
-
-
-
接 :對外提供接口
組件化之間不能直接通訊,需要使用暴露接口的方式對外通訊
-
-
-
測 :反復(fù)測試
重構(gòu)后代碼,需要反復(fù)測試,防止出現(xiàn)意想不到的bug
-
4.組件化重構(gòu)過程
這里我以登錄業(yè)務(wù)ft_login
為例子:
1. 步驟1 :首先新建一個業(yè)務(wù)模塊ft_login
,然后在宿主app中將登錄功能相關(guān)聯(lián)的代碼和資源抽離到ft_login
中
2. 步驟2 :將和登錄構(gòu)建相關(guān)的依賴分配到ft_login
構(gòu)建中。
3. 步驟3 :單獨調(diào)試功能實現(xiàn)
- 3.1:在
gradle.properties
中創(chuàng)建一個全局變量:isRunAlone=true
- 3.2:在
build.gradle
中:
if(isRunAlone.toBoolean()){
apply plugin:'com.android.application'
}else{
apply plugin:'com.android.library'
}
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
defaultConfig {
if(isRunAlone.toBoolean()){
applicationId 'com.anna.ft_login'
}
...
}
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/res']
}
aidl {
srcDirs = ['src/main/aidl']
}
manifest {
if(isRunAlone.toBoolean()){
srcFile 'src/main/manifest/AndroidManifest.xml'
}else {
srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
}
def dependList = [rootProject.depsLibs.okhttp,
rootProject.depsLibs.gson,
rootProject.depsLibs.appcompact,
rootProject.depsLibs.design,
rootProject.depsLibs.eventbus,
rootProject.depsLibs.arouterapi,
':lib_network',':lib_common_ui',':ft_base_service']
dependencies {
if(!isRunAlone.toBoolean()){
dependList.each { String depend ->
depend.startsWithAny(':lib',':ft')? compileOnly(project(depend)):compileOnly(depend){
switch (depend){
case rootProject.depsLibs.arouterapi:
exclude group: 'com.android.support'
break;
}
}
}
}else {
dependList.each { String depend ->
depend.startsWithAny(':lib',':ft')? implementation(project(depend)):implementation(depend) {
switch (depend) {
case rootProject.depsLibs.arouterapi:
exclude group: 'com.android.support'
break;
}
}
}
}
//arouter注解處理器
annotationProcessor rootProject.depsLibs.aroutercompiler
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
單獨調(diào)試狀態(tài)下注意四點 :
- 1.引用application插件
- 2.引入applicationId
- 3.引入不同給的sourceSets構(gòu)建路徑
- 4.引入的庫單獨調(diào)試狀態(tài)下需要使用
implementation
導(dǎo)入,不能使用compileOnly
實現(xiàn)上面四點, 只要打開isRunAlone就可作為一個單獨app運行了 。
4.步驟4:組件間通訊
這里,我們引入一個ft_base_service
模塊,這個模塊用來實現(xiàn)組件間通訊用,需要調(diào)用別的業(yè)務(wù)模塊都需要使用這個模塊才能通訊、
業(yè)務(wù)模塊與ft_base_service
之間通訊使用的是路由ARouter
:
關(guān)于ARouter
的使用可以參考這篇文章:
Android開源系列-組件化框架Arouter-(一)使用方式詳解
- 1.創(chuàng)建
ft_base_service
,在這個模塊中:創(chuàng)建一個LoginService
接口繼承IProvider
引入ARouter依賴
:
android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
//arouter核心api
implementation rootProject.depsLibs.arouterapi
//arouter注解處理器
annotationProcessor rootProject.depsLibs.aroutercompiler
創(chuàng)建LoginService:
public interface LoginService extends IProvider {
boolean hasLogin();
void login(Context context);
}
- 2.在
ft_login
業(yè)務(wù)模塊中實現(xiàn)LoginService接口
注意這里因為使用了ARouter注解,所以也需要引入ARouter
依賴
@Route(path = "/login/login_service")
public class LoginServiceImpl implements LoginService {
Context context;
@Override
public boolean hasLogin() {
return UserManager.getInstance().hasLogined();
}
@Override
public void login(Context context) {
LoginActivity.start(context);
}
@Override
public void init(Context context) {
Log.d("TAG","LoginServiceImpl is init");
}
}
- 3.在
ft_base_service
模塊中對LoginService
接口進(jìn)行依賴注入
public class LoginImpl {
@Autowired(name = "/login/login_service")
public LoginService mLoginService;
private static LoginImpl mLoginImpl = null;
public static LoginImpl getInstance() {
if (mLoginImpl == null) {
synchronized (LoginImpl.class) {
if (mLoginImpl == null) {
mLoginImpl = new LoginImpl();
}
return mLoginImpl;
}
}
return mLoginImpl;
}
private LoginImpl(){
ARouter.getInstance().inject(this);
}
public boolean hasLogin(){
return mLoginService.hasLogin();
}
public void login(Context context){
mLoginService.login(context);
}
}
筆者使用了一個 單例類LoginImpl ,在構(gòu)造器中對LoginService
依賴注入
ARouter.getInstance().inject(this);
然后宿主app或者其他模塊引用登錄業(yè)務(wù)功能時,需要依賴ft_base_service
模塊,并使用LoginImpl
的接口即可。
這里要說明下,平時我們使用的四大組件跳轉(zhuǎn)也可以使用這個方式來處理,在服務(wù)接口中定義跳轉(zhuǎn)接口即可。當(dāng)然也可以使用Arouter的Activity跳轉(zhuǎn)方式或者Fragment實例獲取方式
- 5.代碼打包aar上傳到
maven私服
:
關(guān)于這塊maven私服更多內(nèi)容可以參考這篇文章:
Gradle筑基篇(六)-使用Maven實現(xiàn)組件化類庫發(fā)布
這里我們封裝了一個通用組件發(fā)布庫:
apply plugin: 'maven'
uploadArchives {
repositories {
mavenDeployer {
// 是否快照版本
def isSnapShot = Boolean.valueOf(MAVEN_IS_SNAPSHOT)
def versionName = MAVEN_VERSION
if (isSnapShot) {
versionName += "-SNAPSHOT"
}
// 組件信息
pom.groupId = MAVEN_GROUP_ID
pom.artifactId = MAVEN_ARTIFACTID
pom.version = versionName
// 快照倉庫路徑
snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) {
authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
// 發(fā)布倉庫路徑
repository(url: uri(MAVEN_RELEASE_URL)) {
authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
println("###################################"
+ "\\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging
+ "\\nrepository =" + (isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL)
+ "\\n###################################"
)
}
}
}
然后在對應(yīng)的組件下面引用:
apply from:file('../maven.gradle')
發(fā)布的時候直接在Gradle
面板中點擊uploadArchives
任務(wù)即可
task面板.png
經(jīng)過上面幾個步驟就基本完成了login組件的封裝并發(fā)布,且對外提供了login組件接口 其他組件也是按照上面的邏輯進(jìn)行重構(gòu)
更多詳細(xì)信息可以自己拿到項目源代碼查看。
5.組件化重構(gòu)總結(jié)
組件化不僅是一種架構(gòu),更是一種思想,架構(gòu)是可以變得,但是核心思想?yún)s是統(tǒng)一的,在拆分代碼的時候,要注意模塊的顆粒度,不是顆粒度越小就越好,模塊分離的好,后期對組件改造會有很大幫助, 關(guān)于組件化的文章就講到這里,組件化重構(gòu)的項目已經(jīng)上傳到Github。后面會出一期插件化
的項目改造。敬請期待。
** demo地址: https://github.com/ByteYuhb/anna_music_app **
-
Android
+關(guān)注
關(guān)注
12文章
3917瀏覽量
127042 -
APP
+關(guān)注
關(guān)注
33文章
1566瀏覽量
72334 -
代碼
+關(guān)注
關(guān)注
30文章
4723瀏覽量
68238
發(fā)布評論請先 登錄
相關(guān)推薦
評論