1.組件化重構(gòu)效果
這里先看下我們重構(gòu)前后的框架圖比較:
重構(gòu)前:
傳統(tǒng)代碼架構(gòu).png
重構(gòu)后
組件化代碼架構(gòu).png
ft_xxx
表示業(yè)務(wù)層模塊lib_xxx
表示基礎(chǔ)庫(kù)模塊
重構(gòu)后的架構(gòu)圖如下 :
服務(wù)接口調(diào)用.png
重構(gòu)前的代碼業(yè)務(wù)封裝在宿主app中,業(yè)務(wù)耦合嚴(yán)重,如果修改一個(gè)業(yè)務(wù)模塊,需要對(duì)整個(gè)app進(jìn)行完整測(cè)試,測(cè)試工作量巨大
而重構(gòu)后,我們只需要對(duì)單一app進(jìn)行獨(dú)立調(diào)試即可。
重構(gòu)后的框架結(jié)構(gòu):所有的業(yè)務(wù)組件之間通訊都通過ft_base_service
進(jìn)行通訊
2.組件化重構(gòu)準(zhǔn)則
- 1.單一業(yè)務(wù)可以單獨(dú)調(diào)試,也可以作為lib提供給宿主app使用
- 2.同一級(jí)別的模塊不允許直接調(diào)用,比如我們的ft_home組件不允許直接調(diào)用ft_login組件,不然組件化的意義就不存在了
- 3.組件間通訊不能直接使用顯示的class文件跳轉(zhuǎn),可以考慮很用ARouter框架進(jìn)行解耦
- 4.每個(gè)組件可打包為aar或者jar上傳到maven私服,宿主使用的時(shí)候,直接引用私服中aar包即可
能做到以上幾點(diǎn),你的app就可以稱為一個(gè)組件化框架的app了。
3.組件化重構(gòu)思路
重構(gòu)思路.png
-
-
拆 :拆代碼,拆資源,拆構(gòu)建
由于所有業(yè)務(wù)和資源都耦合在宿主app中,所以需要將代碼和資源拆開到對(duì)應(yīng)模塊中
當(dāng)然我們的構(gòu)建build.gradle也需要拆分到不同模塊中
-
-
-
接 :對(duì)外提供接口
組件化之間不能直接通訊,需要使用暴露接口的方式對(duì)外通訊
-
-
-
測(cè) :反復(fù)測(cè)試
重構(gòu)后代碼,需要反復(fù)測(cè)試,防止出現(xiàn)意想不到的bug
-
4.組件化重構(gòu)過程
這里我以登錄業(yè)務(wù)ft_login
為例子:
1. 步驟1 :首先新建一個(gè)業(yè)務(wù)模塊ft_login
,然后在宿主app中將登錄功能相關(guān)聯(lián)的代碼和資源抽離到ft_login
中
2. 步驟2 :將和登錄構(gòu)建相關(guān)的依賴分配到ft_login
構(gòu)建中。
3. 步驟3 :?jiǎn)为?dú)調(diào)試功能實(shí)現(xiàn)
- 3.1:在
gradle.properties
中創(chuàng)建一個(gè)全局變量: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'
}
單獨(dú)調(diào)試狀態(tài)下注意四點(diǎn) :
- 1.引用application插件
- 2.引入applicationId
- 3.引入不同給的sourceSets構(gòu)建路徑
- 4.引入的庫(kù)單獨(dú)調(diào)試狀態(tài)下需要使用
implementation
導(dǎo)入,不能使用compileOnly
實(shí)現(xiàn)上面四點(diǎn), 只要打開isRunAlone就可作為一個(gè)單獨(dú)app運(yùn)行了 。
4.步驟4:組件間通訊
這里,我們引入一個(gè)ft_base_service
模塊,這個(gè)模塊用來實(shí)現(xiàn)組件間通訊用,需要調(diào)用別的業(yè)務(wù)模塊都需要使用這個(gè)模塊才能通訊、
業(yè)務(wù)模塊與ft_base_service
之間通訊使用的是路由ARouter
:
關(guān)于ARouter
的使用可以參考這篇文章:
Android開源系列-組件化框架Arouter-(一)使用方式詳解
- 1.創(chuàng)建
ft_base_service
,在這個(gè)模塊中:創(chuàng)建一個(gè)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ù)模塊中實(shí)現(xiàn)LoginService接口
注意這里因?yàn)槭褂昧薃Router注解,所以也需要引入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
模塊中對(duì)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);
}
}
筆者使用了一個(gè) 單例類LoginImpl ,在構(gòu)造器中對(duì)LoginService
依賴注入
ARouter.getInstance().inject(this);
然后宿主app或者其他模塊引用登錄業(yè)務(wù)功能時(shí),需要依賴ft_base_service
模塊,并使用LoginImpl
的接口即可。
這里要說明下,平時(shí)我們使用的四大組件跳轉(zhuǎn)也可以使用這個(gè)方式來處理,在服務(wù)接口中定義跳轉(zhuǎn)接口即可。當(dāng)然也可以使用Arouter的Activity跳轉(zhuǎn)方式或者Fragment實(shí)例獲取方式
- 5.代碼打包aar上傳到
maven私服
:
關(guān)于這塊maven私服更多內(nèi)容可以參考這篇文章:
Gradle筑基篇(六)-使用Maven實(shí)現(xiàn)組件化類庫(kù)發(fā)布
這里我們封裝了一個(gè)通用組件發(fā)布庫(kù):
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
// 快照倉(cāng)庫(kù)路徑
snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) {
authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
// 發(fā)布倉(cāng)庫(kù)路徑
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###################################"
)
}
}
}
然后在對(duì)應(yīng)的組件下面引用:
apply from:file('../maven.gradle')
發(fā)布的時(shí)候直接在Gradle
面板中點(diǎn)擊uploadArchives
任務(wù)即可
task面板.png
經(jīng)過上面幾個(gè)步驟就基本完成了login組件的封裝并發(fā)布,且對(duì)外提供了login組件接口 其他組件也是按照上面的邏輯進(jìn)行重構(gòu)
更多詳細(xì)信息可以自己拿到項(xiàng)目源代碼查看。
5.組件化重構(gòu)總結(jié)
組件化不僅是一種架構(gòu),更是一種思想,架構(gòu)是可以變得,但是核心思想?yún)s是統(tǒng)一的,在拆分代碼的時(shí)候,要注意模塊的顆粒度,不是顆粒度越小就越好,模塊分離的好,后期對(duì)組件改造會(huì)有很大幫助, 關(guān)于組件化的文章就講到這里,組件化重構(gòu)的項(xiàng)目已經(jīng)上傳到Github。后面會(huì)出一期插件化
的項(xiàng)目改造。敬請(qǐng)期待。
** demo地址: https://github.com/ByteYuhb/anna_music_app **
-
Android
+關(guān)注
關(guān)注
12文章
3939瀏覽量
127598 -
APP
+關(guān)注
關(guān)注
33文章
1575瀏覽量
72586 -
代碼
+關(guān)注
關(guān)注
30文章
4803瀏覽量
68760
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論