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
里添加布爾類(lèi)型選項(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)的相互引用來(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è)類(lèi)實(shí)現(xiàn)自己對(duì)應(yīng)的 Service 中的抽象方法。在組件加載后,需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)類(lèi)的對(duì)象,然后將實(shí)現(xiàn)了 Service 的類(lèi)的對(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)類(lèi)對(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)部類(lè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)用來(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類(lèi)
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 類(lèi)會(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 類(lèi)強(qiáng)引用到主 Module 的 Application 中進(jìn)行初始化,這就必須要求主模塊可以直接訪問(wèn)組件中的類(lèi)。而我們又不想在開(kāi)發(fā)過(guò)程中主模塊能訪問(wèn)組件中的類(lèi),這里可以通過(guò)反射來(lái)實(shí)現(xiàn)組件 Application 的初始化。
1.定義抽象類(lèi) 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 類(lèi)
在Baselibs
模塊定義一個(gè)靜態(tài)的 String 數(shù)組,我們將需要初始化的組件的 Application 的完整類(lèi)名放入到這個(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 類(lèi)的方式實(shí)現(xiàn) Fragment 的實(shí)例化,但是現(xiàn)在為了實(shí)現(xiàn)模塊與組件間的解耦,在移除組件時(shí)不會(huì)由于引用的 Fragment 不存在而編譯失敗,我們就不能模塊中直接訪問(wèn)組件的 Fragment 類(lèi)。
這里介紹兩種方法
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)類(lèi)中實(shí)現(xiàn)這個(gè)方法,然后在主模塊中通過(guò) ServiceFactory
獲取 LoginService
的實(shí)現(xiàn)類(lèi)對(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全局監(jiān)控click事件的四種方式(小結(jié))
本篇文章主要介紹了android全局監(jiān)控click事件的四種方式(小結(jié)),詳細(xì)介紹如何在全局上去監(jiān)聽(tīng) click 點(diǎn)擊事件,并做些通用處理或是攔截,有興趣的可以了解一下2017-08-08Android Spinner 組件的應(yīng)用實(shí)例
這篇文章主要介紹了Android Spinner 組件的應(yīng)用實(shí)例的相關(guān)資料,希望通過(guò)本文大家能夠掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09Android retrofit上傳文件實(shí)例(包含頭像)
下面小編就為大家分享一篇Android retrofit上傳文件實(shí)例(包含頭像),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Android沉浸式狀態(tài)欄設(shè)計(jì)的實(shí)例代碼
本篇文章主要介紹了Android沉浸式狀態(tài)欄設(shè)計(jì)的實(shí)例代碼,整理了詳細(xì)的代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07詳解Android廣播Broadcast的啟動(dòng)流程
這篇文章主要為大家介紹了Android廣播Broadcast啟動(dòng)流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Android基于虹軟(ArcSoft)實(shí)現(xiàn)人臉識(shí)別
人工智能時(shí)代快速來(lái)臨,其中人臉識(shí)別是當(dāng)前比較熱門(mén)的技術(shù),在國(guó)內(nèi)也越來(lái)越多的運(yùn)用,例如刷臉打卡,刷臉APP,身份識(shí)別,人臉門(mén)禁等。本文將為大家介紹Android基于虹軟(ArcSoft)實(shí)現(xiàn)人臉識(shí)別的demo,快來(lái)跟隨小編一起學(xué)習(xí)吧2021-12-12Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼
本篇文章主要介紹了Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09Android中View.post和Handler.post的關(guān)系
這篇文章主要介紹了Android中View.post和Handler.post的關(guān)系,View.post和Handler.post是Android開(kāi)發(fā)中經(jīng)常使用到的兩個(gè)”post“方法,關(guān)于兩者存在的區(qū)別與聯(lián)系,文章詳細(xì)分析需要的小伙伴可以參考一下2022-06-06