Android組件化原理詳細(xì)介紹
什么是組件化?
一個(gè)大型APP版本一定會(huì)不斷的迭代,APP里的功能也會(huì)隨之增加,項(xiàng)目的業(yè)務(wù)也會(huì)變的越來(lái)越復(fù)雜,這樣導(dǎo)致項(xiàng)目代碼也變的越來(lái)越多,開(kāi)發(fā)效率也會(huì)隨之下降。并且單一工程下代碼耦合嚴(yán)重,每修改一處代碼后都要重新編譯,非常耗時(shí),單獨(dú)修改的一個(gè)模塊無(wú)法單獨(dú)測(cè)試。
組件化架構(gòu)的目的是讓各個(gè)業(yè)務(wù)變得相對(duì)獨(dú)立,各個(gè)組件在組件模式下可以獨(dú)立開(kāi)發(fā)調(diào)試,集成模式下又可以集成到“app殼工程”中,從而得到一個(gè)具有完整功能的APP。
組件化每一個(gè)組件都可以是一個(gè)APP可以單獨(dú)修改調(diào)試,而不影響總項(xiàng)目。

為什么使用組件化?
編譯速度: 可以但需測(cè)試單一模塊,極大提高了開(kāi)發(fā)速度
超級(jí)解耦: 極度降低了模塊間的耦合,便于后期的維護(hù)和更新
功能重用: 某一塊的功能在另外的組件化項(xiàng)目中使用只需要單獨(dú)依賴這一模塊即可
便于團(tuán)隊(duì)開(kāi)發(fā): 組件化架構(gòu)是團(tuán)隊(duì)開(kāi)發(fā)必然會(huì)選擇的一種開(kāi)發(fā)方式,它能有效的使團(tuán)隊(duì)更好的協(xié)作
一步步搭建組件化
這里以演示為例,只設(shè)置登錄這一個(gè)功能組件
組件化開(kāi)發(fā)要注意的幾點(diǎn)問(wèn)題 :
- 要注意包名和資源文件命名沖突問(wèn)題
Gradle中的版本號(hào)的統(tǒng)一管理- 組件在
AppIication和Library之間如何做到隨意切換 AndroidManifest. xml文件的區(qū)分Library不能在Gradle文件中有applicationId
這里以演示為例,只設(shè)置登錄和個(gè)人中心這兩個(gè)功能組件
1.新建模塊

并且在module里新建一個(gè)activity

到這里我們看到login和我們的app都在有一個(gè)綠點(diǎn)證明創(chuàng)建成功

個(gè)人中心member模塊創(chuàng)建同理,并且每個(gè)模塊目前都可以獨(dú)立運(yùn)行。

2.統(tǒng)一Gradle版本號(hào)
每一個(gè)模塊都是一個(gè)application,所以每個(gè)模塊都會(huì)有一個(gè)build.gradle,各個(gè)模塊里面的配置不同,我們需要重新統(tǒng)一Gradle
在主模塊創(chuàng)建config.gradle

在config.gradle里去添加一些版本號(hào)
ext{
android = [
compileSdkVersion :30,
buildToolsVersion: "30.0.2",
applicationId :"activitytest.com.example.moduletest",
minSdkVersion: 29,
targetSdkVersion :30,
versionCode :1,
versionName :"1.0",
]
androidxDeps = [
"appcompat": 'androidx.appcompat:appcompat:1.1.0',
"material": 'com.google.android.material:material:1.1.0',
"constaraintlayout": 'androidx.constraintlayout:constraintlayout:1.1.3',
]
commonDeps = [
"arouter_api" : 'com.alibaba:arouter-api:1.5.1',
"glide" : 'com.github.bumptech.glide:glide:4.11.0'
]
annotationDeps = [
"arouter_compiler" : 'com.alibaba:arouter-compiler:1.5.1'
]
retrofitDeps = [
"retrofit" : 'com.squareup.retrofit2:retrofit:2.9.0',
"converter" : 'com.squareup.retrofit2:converter-gson:2.9.0',
"rxjava" : 'io.reactivex.rxjava2:rxjava:2.2.20',
"rxandroid" : 'io.reactivex.rxjava2:rxandroid:2.1.1',
"adapter" : 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
]
androidxLibs = androidxDeps.values()
commonLibs = commonDeps.values()
annotationLibs = annotationDeps.values()
retrofitLibs = retrofitDeps.values()
}在主模塊的build.gradle里添加
apply from: "config.gradle"

在各模塊中去引用這些版本號(hào)
引用格式如下,兩種寫(xiě)法均可
compileSdkVersion rootProject.ext.android["compileSdkVersion"] buildToolsVersion rootProject.ext.android.buildToolsVersion
引用前:

引用后:

并且使用同樣的方法,我們還可以統(tǒng)一我們的依賴庫(kù)在config.gradle里去添加我們要依賴的庫(kù),并在各個(gè)模塊中去添加依賴
implementation rootProject.ext.dependencies.publicImplementation
也可以采用第二種寫(xiě)法
dependencies = [
"appcompat" : 'androidx.appcompat:appcompat:1.2.0',
"material" : 'com.google.android.material:material:1.2.1',
"constraintLayout" : 'androidx.constraintlayout:constraintlayout:2.0.4',//約束性布局
//test
"junit" : "junit:junit:4.13.1",
"testExtJunit" : 'androidx.test.ext:junit:1.1.2',//測(cè)試依賴,新建項(xiàng)目時(shí)會(huì)默認(rèn)添加,一般不建議添加
"espressoCore" : 'androidx.test.espresso:espresso-core:3.3.0',//測(cè)試依賴,新建項(xiàng)目時(shí)會(huì)默認(rèn)添加,一般不建議添加
]添加依賴:
dependencies {
implementation rootProject.ext.dependencies.appcompat
implementation rootProject.ext.dependencies["constraintLayout"]
testImplementation rootProject.ext.dependencies["junit"]
androidTestImplementation rootProject.ext.dependencies["testExtJunit"]
androidTestImplementation rootProject.ext.dependencies["espressoCore"]
}3.創(chuàng)建基礎(chǔ)庫(kù)
和新建module一樣,這里需要新建一個(gè)library我們把它命名為Baselibs

同樣需要統(tǒng)一版本號(hào),由于這是一個(gè)library模塊,所以它不需要applicationId

我們一樣可以把它寫(xiě)進(jìn)config.gradle
other:[path:':Baselibs']
在每個(gè)模塊去調(diào)用
implementation project(rootProject.ext.dependencies.other)
同理,當(dāng)本地庫(kù)為單獨(dú)所用,我們可以直接調(diào)用,而不需要將其寫(xiě)入config.gradle,兩種方法選擇合適使用即可。
implementation project(':Baselibs')但有時(shí)因?yàn)?code>gradle版本問(wèn)題,我們可能無(wú)法依賴到這些公共庫(kù),因?yàn)槲覀冊(cè)?code>config.gradle里是以數(shù)組形式定義的,這時(shí)我們可以同for-each循環(huán)的方法將其依次導(dǎo)入config.gradle里
dependencies = [
......
other:[':Baselibs']
]其他模塊的build.gradle
dependencies {
......
rootProject.ext.dependencies.other.each{
implementation project(it)
}4.組件模式和集成模式轉(zhuǎn)換
在主模塊gradle.properties里添加布爾類型選項(xiàng)。

在各個(gè)模塊的build.gradle里添加更改語(yǔ)句
if(is_Module.toBoolean()){
apply plugin: 'com.android.application'
}else{
apply plugin: 'com.android.library'
}每個(gè)模塊的applicationId也需要處理
if(is_Module.toBoolean()){
applicationId "activitytest.com.example.login"
}
當(dāng)我們將is_module改為false時(shí),再次運(yùn)行編譯器我們的模塊都不能單獨(dú)運(yùn)行了

在app模塊中添加判斷依賴就可以在集成模式下將各模塊添加到app主模塊中
// 每加入一個(gè)新的模塊,就需要在下面對(duì)應(yīng)的添加一行
if (is_Module.toBoolean())]) {
implementation project(path:':login')
implementation project(path:':member')
}5.AndroidManifest的切換
為了單獨(dú)開(kāi)發(fā)加載不同的AndroidManifest這里需要重新區(qū)分下。
在組件模塊里的main文件里新建manifest文件夾

并且重寫(xiě)一個(gè)AndroidManifest.xml文件,集成模式下,業(yè)務(wù)組件的表單是絕對(duì)不能擁有自己的 Application 和 launch 的 Activity的,也不能聲明APP名稱、圖標(biāo)等屬性,總之a(chǎn)pp殼工程有的屬性,業(yè)務(wù)組件都不能有,在這個(gè)表單中只聲明了應(yīng)用的主題,而且這個(gè)主題還是跟app殼工程中的主題是一致的
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login">
<application
android:theme="@style/Theme.MoudleTest">
<activity android:name=".LoginActivity">
</activity>
</application>
</manifest>并且我們還要使其在不同的模式下加載不同的AndroidManifest只需在各模塊的build.gradle里添加更改語(yǔ)句
sourceSets {
main {
if (is_Module.toBoolean()) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/mainfest/AndroidManifest.xml'
}
}
}6.*業(yè)務(wù)Application切換
每個(gè)模塊在運(yùn)行時(shí)都會(huì)有自己的application,而在組件化開(kāi)發(fā)過(guò)程中,我們的主模塊只能有一個(gè)application,但在單獨(dú)運(yùn)行時(shí)又需要自己的application這里就需要配置一下。
在業(yè)務(wù)模塊添加新文件夾命名module

在里面建一個(gè)application文件

并且我們?cè)?code>build.gradle文件里配置module文件夾使其在單獨(dú)運(yùn)行時(shí)能夠運(yùn)行單獨(dú)的application
在配置manifest的語(yǔ)句中添加java.srcDir 'src/main/module'
sourceSets {
main {
if (is_Module.toBoolean()) {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDir 'src/main/module'
} else {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}
}
}同時(shí)我們?cè)?code>basic基礎(chǔ)層內(nèi)新建application,用于加載一些數(shù)據(jù)的初始化
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.e("fff","baseapplication");
}
}在業(yè)務(wù)模塊內(nèi)module里重寫(xiě)該模塊的application
public class LoginApplication extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
}
}至此,組件化框架搭建結(jié)束
組件之間的跳轉(zhuǎn)
這里采用阿里巴巴的開(kāi)源庫(kù)ARouter來(lái)實(shí)現(xiàn)跳轉(zhuǎn)功能,我會(huì)在以后的文章單獨(dú)拿出一篇來(lái)一步步去解讀Arouter源碼,讓我們自己去搭建一個(gè)自己的路由
一個(gè)用于幫助 Android App 進(jìn)行組件化改造的框架 —— 支持模塊間的路由、通信、解耦
由 github 上 ARouter 的介紹可以知道,它可以實(shí)現(xiàn)組件間的路由功能。路由是指從一個(gè)接口上收到數(shù)據(jù)包,根據(jù)數(shù)據(jù)路由包的目的地址進(jìn)行定向并轉(zhuǎn)發(fā)到另一個(gè)接口的過(guò)程。這里可以體現(xiàn)出路由跳轉(zhuǎn)的特點(diǎn),非常適合組件化解耦。
要使用 ARouter 進(jìn)行界面跳轉(zhuǎn),需要我們的組件對(duì) Arouter 添加依賴,因?yàn)樗械慕M件都依賴了 Baselibs模塊,所以我們?cè)?Baselibs 模塊中添加 ARouter 的依賴即可。其它組件共同依賴的庫(kù)也最好都放到 Baselibs中統(tǒng)一依賴。
這里需要注意的是,arouter-compiler 的依賴需要所有使用到 ARouter 的模塊和組件中都單獨(dú)添加,不然無(wú)法在 apt 中生成索引文件,也就無(wú)法跳轉(zhuǎn)成功。并且在每一個(gè)使用到 ARouter 的模塊和組件的 build.gradle 文件中,其 android{} 中的 javaCompileOptions 中也需要添加特定配置。
1.添加依賴
在Baselibs里的build.gradle添加依賴
dependencies {
api 'com.alibaba:arouter-api:1.3.1'
// arouter-compiler 的注解依賴需要所有使用 ARouter 的 module 都添加依賴
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
// 所有使用到 ARouter 的組件和模塊的 build.gradle
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
dependencies {
...
implementation project (':base')
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
主模塊需要對(duì)跳轉(zhuǎn)模塊進(jìn)行依賴:
// 主項(xiàng)目的 build.gradle 需要添加對(duì) login 組件和 share 組件的依賴
dependencies {
// ... 其他
implementation project(':login')
implementation project(':share')
}2.初始化ARouter
添加了對(duì) ARouter 的依賴后,還需要在項(xiàng)目的 Application 中將 ARouter 初始化,我們這里將 ARouter 的初始化工作放到主模塊Application 的 onCreate()方法中,在應(yīng)用啟動(dòng)的同時(shí)將 ARouter 初始化。
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 初始化 ARouter
if (isDebug()) {
// 這兩行必須寫(xiě)在init之前,否則這些配置在init過(guò)程中將無(wú)效
// 打印日志
ARouter.openLog();
// 開(kāi)啟調(diào)試模式(如果在InstantRun模式下運(yùn)行,必須開(kāi)啟調(diào)試模式!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
ARouter.openDebug();
}
// 初始化 ARouter
ARouter.init(this);
}
private boolean isDebug() {
return BuildConfig.DEBUG;
}
}3.添加跳轉(zhuǎn)
這里我們?cè)谑醉?yè)添加登錄和分享兩個(gè)跳轉(zhuǎn)頁(yè)面。
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build("/login/login").navigation();
}
});share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build("/share/share").navigation();
}
});然后,需要在登錄和分享組件中分別添加 LoginActivity 和 ShareActivity ,然后分別為兩個(gè) Activity 添加注解 Route,其中path 是跳轉(zhuǎn)的路徑,這里的路徑需要注意的是至少需要有兩級(jí),/xx/xx
@Route(path = "/login/login")
public class Login extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
}@Route(path = "/share/share")
public class Share extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share);
}
}這樣就可以實(shí)現(xiàn)跳轉(zhuǎn)了。
組件之間的數(shù)據(jù)傳遞
由于主項(xiàng)目與組件,組件與組件之間都是不可以直接使用類的相互引用來(lái)進(jìn)行數(shù)據(jù)傳遞的,那么在開(kāi)發(fā)過(guò)程中如果有組件間的數(shù)據(jù)傳遞時(shí)應(yīng)該如何解決呢,這里我們可以采用 [接口 + 實(shí)現(xiàn)] 的方式來(lái)解決。
在Baselibs基礎(chǔ)庫(kù)里定義組件可以對(duì)外提供訪問(wèn)自身數(shù)據(jù)的抽象方法的 Service。并且提供了一個(gè) ServiceFactory,每個(gè)組件中都要提供一個(gè)類實(shí)現(xiàn)自己對(duì)應(yīng)的 Service 中的抽象方法。在組件加載后,需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)類的對(duì)象,然后將實(shí)現(xiàn)了 Service 的類的對(duì)象添加到ServiceFactory 中。這樣在不同組件交互時(shí)就可以通過(guò) ServiceFactory 獲取想要調(diào)用的組件的接口實(shí)現(xiàn),然后調(diào)用其中的特定方法就可以實(shí)現(xiàn)組件間的數(shù)據(jù)傳遞與方法調(diào)用。
當(dāng)然,ServiceFactory 中也會(huì)提供所有的 Service 的空實(shí)現(xiàn),在組件單獨(dú)調(diào)試或部分集成調(diào)試時(shí)避免出現(xiàn)由于實(shí)現(xiàn)類對(duì)象為空引起的空指針異常。
下面我們就按照這個(gè)方法來(lái)解決組件間數(shù)據(jù)傳遞與方法的相互調(diào)用這個(gè)問(wèn)題,這里我們通過(guò)分享組件 中調(diào)用 登錄組件 中的方法來(lái)獲取登錄狀態(tài)是否登錄這個(gè)場(chǎng)景來(lái)演示。
1.定義接口
其中 service文件夾中定義接口,LoginService 接口中定義了 Login 組件向外提供的數(shù)據(jù)傳遞的接口方法,EmptyService 中是 service 中定義的接口的空實(shí)現(xiàn),ServiceFactory 接收組件中實(shí)現(xiàn)的接口對(duì)象的注冊(cè)以及向外提供特定組件的接口實(shí)現(xiàn)。

LoginService
public interface LoginService {
/**
* 是否已經(jīng)登錄
* @return
*/
boolean isLogin();
/**
* 獲取登錄用戶的 Password
* @return
*/
String getPassword();
}
EmptyService
public class EmptyService implements LoginService {
@Override
public boolean isLogin() {
return false;
}
@Override
public String getPassword() {
return null;
}
}ServiceFactory
public class ServiceFactory {
private LoginService loginService;
private ServiceFactory(){
/**
* 禁止外部創(chuàng)建 ServiceFactory 對(duì)象
*/
private ServiceFactory() {
}
/**
* 通過(guò)靜態(tài)內(nèi)部類方式實(shí)現(xiàn) ServiceFactory 的單例
*/
public static ServiceFactory getInstance() {
return Inner.serviceFactory;
}
private static class Inner {
private static ServiceFactory serviceFactory = new ServiceFactory();
}
/**
* 接收 Login 組件實(shí)現(xiàn)的 Service 實(shí)例
*/
public void setLoginService(LoginService loginService){
this.loginService = loginService;
}
/**
* 返回 Login 組件的 Service 實(shí)例
*/
public LoginService getLoginService(){
if(loginService == null){
return new EmptyService();
}else{
return loginService;
}
}
}2.實(shí)現(xiàn)接口
在login模塊
public class AccountService implements LoginService {
private boolean login;
private String password;
public AccountService(boolean login, String password) {
this.login = login;
this.password = password;
}
@Override
public boolean isLogin() {
return login;
}
@Override
public String getPassword() {
return password;
}
}這里新建一個(gè)Util類用來(lái)存儲(chǔ)登錄數(shù)據(jù)
public class LoginUtil {
static boolean isLogin = false;
static String password = null;
}實(shí)現(xiàn)一下登錄操作
login = (Button)findViewById(R.id.login);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LoginUtil.isLogin = true;
LoginUtil.password = "admin";
ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
}
});在login模塊的application里定義ServiceFactory類
public class LoginApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
}
}在分享模塊獲取登錄信息
share = (Button)findViewById(R.id.share);
share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ServiceFactory.getInstance().getLoginService().isLogin()){
Toast.makeText(ShareActivity.this,"分享成功!",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(ShareActivity.this,"分享失敗,請(qǐng)先登錄!",Toast.LENGTH_SHORT).show();
}
}
});一個(gè)項(xiàng)目時(shí)只能有一個(gè) Application 的,Login 作為組件時(shí),主模塊的 Application 類會(huì)初始化,而 Login 組件中的 Applicaiton 不會(huì)初始化。確實(shí)是存在這個(gè)問(wèn)題的,我們這里先將 Service 的注冊(cè)放到其活動(dòng)里,稍后我們會(huì)解決 Login 作為組件時(shí) Appliaciton 不會(huì)初始化的問(wèn)題。
組件Application的動(dòng)態(tài)切換
在主模塊中有 Application 等情況下,組件在集中調(diào)試時(shí)其 Applicaiton 不會(huì)初始化的問(wèn)題。而我們組件的 Service 在 ServiceFactory 的注冊(cè)又必須放到組件初始化的地方。
為了解決這個(gè)問(wèn)題可以將組件的 Service 類強(qiáng)引用到主 Module 的 Application 中進(jìn)行初始化,這就必須要求主模塊可以直接訪問(wèn)組件中的類。而我們又不想在開(kāi)發(fā)過(guò)程中主模塊能訪問(wèn)組件中的類,這里可以通過(guò)反射來(lái)實(shí)現(xiàn)組件 Application 的初始化。
1.定義抽象類 BaseApplication 繼承 Application
在Baselibs基礎(chǔ)庫(kù)模塊
public abstract class BaseApplication extends Application {
/**
* Application 初始化
*/
public abstract void initModuleApp(Application application);
/**
* 所有 Application 初始化后的自定義操作
*/
public abstract void initModuleData(Application application); //其他需要調(diào)用的方法
}2.所有的組件的 Application 都繼承 BaseApplication
這里我們以Login模塊為例
public class LoginApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
initModuleApp(this);
initModuleData(this);
}
@Override
public void initModuleApp(Application application) {
ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
}
@Override
public void initModuleData(Application application) {
}
}3.定義 AppConfig 類
在Baselibs模塊定義一個(gè)靜態(tài)的 String 數(shù)組,我們將需要初始化的組件的 Application 的完整類名放入到這個(gè)數(shù)組中。
public class AppConfig {
private static final String LoginApp = "com.example.login.LoginApplication";
public static String[] moduleApps = {
LoginApp
};
}4.主模塊application實(shí)現(xiàn)兩個(gè)初始化方法
// 主 Module 的 Applicaiton
public class MainApplication extends BaseApp {
@Override
public void onCreate() {
super.onCreate();
// 初始化組件 Application
initModuleApp(this);
// 其他操作
// 所有 Application 初始化后的操作
initModuleData(this);
}
@Override
public void initModuleApp(Application application) {
for (String moduleApp : AppConfig.moduleApps) {
try {
Class clazz = Class.forName(moduleApp);
BaseApp baseApp = (BaseApp) clazz.newInstance();
baseApp.initModuleApp(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
@Override
public void initModuleData(Application application) {
for (String moduleApp : AppConfig.moduleApps) {
try {
Class clazz = Class.forName(moduleApp);
BaseApp baseApp = (BaseApp) clazz.newInstance();
baseApp.initModuleData(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}到這里我們就通過(guò)反射,完成了組件 Application 的初始化操作,也實(shí)現(xiàn)了組件與化中的解耦需求。
主模塊使用其他組件的 Fragment
我們?cè)陂_(kāi)發(fā)過(guò)程中經(jīng)常使用 Fragment。一般情況下,我們都是直接通過(guò)訪問(wèn)具體 Fragment 類的方式實(shí)現(xiàn) Fragment 的實(shí)例化,但是現(xiàn)在為了實(shí)現(xiàn)模塊與組件間的解耦,在移除組件時(shí)不會(huì)由于引用的 Fragment 不存在而編譯失敗,我們就不能模塊中直接訪問(wèn)組件的 Fragment 類。
這里介紹兩種方法
1.ARouter
這里可以采用ARouter直接調(diào)用
fragment = (Fragment) ARouter.getInstance().build("/login/fragment").navigation();2.反射
我們還是以Login模塊為例,假如在該模塊創(chuàng)建一個(gè)用戶界面,命名為UserFragment
首先,在 Login組件中創(chuàng)建 UserFragment,然后在 LoginService 接口中添加newUserFragment方法返回一個(gè)Fragment,在Login組件中的 AccountService 和 Baselibs 中 LoginService 的空實(shí)現(xiàn)類中實(shí)現(xiàn)這個(gè)方法,然后在主模塊中通過(guò) ServiceFactory 獲取 LoginService 的實(shí)現(xiàn)類對(duì)象,調(diào)用其 newUserFragment 即可獲取到 UserFragment 的實(shí)例。
// Baselibs 模塊的 LoginService
public interface LoginService {
//其他代碼...
Fragment newUserFragment(Activity activity, int containerId, FragmentManager manager, Bundle bundle, String tag);
}// Login 組件中的 AccountService
public class AccountService implements LoginService {
// 其他代碼 ...
@Override
public Fragment newUserFragment(Activity activity, int containerId, FragmentManager manager, Bundle bundle, String tag) {
FragmentTransaction transaction = manager.beginTransaction();
// 創(chuàng)建 UserFragment 實(shí)例,并添加到 Activity 中
Fragment userFragment = new UserFragment();
transaction.add(containerId, userFragment, tag);
transaction.commit();
return userFragment;
}
}// 主模塊的 FragmentActivity
public class FragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
// 通過(guò)組件提供的 Service 實(shí)現(xiàn) Fragment 的實(shí)例化
ServiceFactory.getInstance().getAccountService().newUserFragment(this, R.id.layout_fragment, getSupportFragmentManager(), null, "");
}
}到此這篇關(guān)于Android組件化原理詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Android組件化 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android應(yīng)用啟動(dòng)流程之從啟動(dòng)到可交互的過(guò)程解析
這篇文章將給大家總結(jié)學(xué)習(xí)Android 基礎(chǔ)知識(shí),Android應(yīng)用啟動(dòng)流程,從啟動(dòng)到可交互的過(guò)程解析,在學(xué)習(xí)過(guò)程中,大家最好是把源碼下載下來(lái),感興趣的小伙伴跟著小編一起來(lái)看看吧2023-08-08
android private libraries 中的包源代碼添加方法
這篇文章主要介紹了android private libraries 中的包源代碼添加方法,方法很簡(jiǎn)單,看完本文即可學(xué)會(huì),需要的朋友可以參考下2015-05-05
Android高亮引導(dǎo)控件的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android高亮引導(dǎo)控件的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Android數(shù)據(jù)庫(kù)Room的實(shí)際使用過(guò)程總結(jié)
這篇文章主要給大家介紹了關(guān)于Android數(shù)據(jù)庫(kù)Room的實(shí)際使用過(guò)程,詳細(xì)介紹了如何創(chuàng)建實(shí)體類、數(shù)據(jù)訪問(wèn)對(duì)象(DAO)和數(shù)據(jù)庫(kù)抽象類,需要的朋友可以參考下2025-01-01
android自定義按鈕示例(重寫(xiě)imagebutton控件實(shí)現(xiàn)圖片按鈕)
由于項(xiàng)目這種類型的圖片按鈕比較多,所以重寫(xiě)了ImageButton類,現(xiàn)在把代碼分享給大家,需要的朋友可以參考下2014-03-03
Android百度地圖應(yīng)用開(kāi)發(fā)基礎(chǔ)知識(shí)
這篇文章主要為大家詳細(xì)介紹了Android百度地圖應(yīng)用開(kāi)發(fā)基礎(chǔ)知識(shí),為開(kāi)發(fā)百度地圖應(yīng)用做準(zhǔn)備,感興趣的小伙伴們可以參考一下2016-06-06
Android調(diào)用相機(jī)并將照片存儲(chǔ)到sd卡上實(shí)現(xiàn)方法
Android中實(shí)現(xiàn)拍照有兩種方法,一種是調(diào)用系統(tǒng)自帶的相機(jī),還有一種是自己用Camera類和其他相關(guān)類實(shí)現(xiàn)相機(jī)功能,這種方法定制度比較高,需要的朋友可以了解下2012-12-12
Android 嵌套Fragment的使用實(shí)例代碼
本文主要介紹Android Fragment,在這里提供了實(shí)例代碼跟效果圖,希望能幫助有需要的小伙伴2016-07-07
Android 開(kāi)發(fā)隨手筆記之使用攝像頭拍照
在Android中,使用攝像頭拍照一般有兩種方法, 一種是調(diào)用系統(tǒng)自帶的Camera,另一種是自己寫(xiě)一個(gè)攝像的界面,本篇文章給大家介紹android開(kāi)發(fā)隨手筆記之使用攝像頭拍照,感興趣的朋友一起學(xué)習(xí)吧2015-11-11

