Android中的Serializable、Parcelable">Android中的Serializable、Parcelable
2013
2025-04-02
公眾號「帥次」:分享 Android 相關知識·面試題庫。
目錄
一、組件化
1.1 為什么使用組件化
1.2 模塊化與組件化
1.2.1 模塊
1.2.2 組件
1.3 組件化的優勢
1.4 組件化需要解決的問題
二、組件分層
2.1 基礎組件
2.2 common組件(lib_common)
2.3 功能組件
2.4 業務組件
2.5 主工程(app)
2.6 完成后項目
三、組件單獨調試
3.1 創建組件()
3.2 動態配置組件的工程類型
3.2.1 build.gradle(module)
3.2.2 設置gradle.properties
3.2.3 動態配制插件(build.gradle)
3.3 動態配置組件的 ApplicationId 和 AndroidManifest 文件
3.3.1 準備兩個不同路徑的 AndroidManifest 文件
3.3.2 src/main/debug/AndroidManifest
3.3.3 src/main/debug/AndroidManifest
3.3.4 動態配制(build.gradle)
3.4 實現效果
3.4.1 獨立調試
3.4.2 集成調試
四、Gradle配置統一管理
4.1 config.gradle
4.2 添加配制文件build.gradle(project)
4.3 其他組件使用
五、組件間界面跳轉(ARouter)
5.1 介紹
5.2 使用
5.2.1 添加依賴
5.2.2 添加注解
5.2.3 初始化SDK(主項目Application)
5.3 發起路由操作
5.3.1 應用內簡單的跳轉
5.3.2 跳轉并攜帶參數
5.3.3 小記(ARouter目標不存在)
六、組件間通信(數據傳遞)
6.1 通過依賴注入解耦:服務管理(一) 暴露服務
6.1.1 創建 componentbase 模塊(lib)
6.1.2 創建接口并繼承IProvider
6.1.3 在module_login組件中實現接口
6.2 通過依賴注入解耦:服務管理(二) 發現服務
6.2.1 在module_main中調用調用是否已登入
七、總結
相關推薦
參考與感謝
一、組件化
作為一個單工程擼到底的開發人員,想試著將項目進行組件化改造,說動就動。畢竟技術都是寫出來的,看看感覺懂了,但是實際開發中還是能遇到各種各樣的問題,開始搞起來。
1.1 為什么使用組件化
一直使用單工程操作,項目越來越大導致出現了不少的問題:
查找問題慢:定位問題,需要在多個代碼混合的模塊中尋找和跳轉。
開發維護成本增加:避免代碼的改動影響其它業務的功能,導致開發和維護成本不斷增加。
編譯時間長:項目工程越大,編譯完整代碼所花費的時間越長。
開發效率低:多人協作開發時,開發風格不一,又很難將業務完全分割,大家互相影響,導致開發效率低下。
代碼復用性差:寫過的代碼很難抽離出來再次利用。
1.2 模塊化與組件化
1.2.1 模塊
將一個程序按照其功能做拆分,分成相互獨立的模塊,以便于每個模塊只包含與其功能相關的內容,比如登錄模塊、首頁模塊等等。
1.2.2 組件
組件指的是單一的功能組件,如登錄組件、視頻組件、支付組件 等,每個組件都可以以一個單獨的 module 開發,并且可以單獨抽出來作為 SDK 對外發布使用??梢哉f往往一個模塊包含了一個或多個組件。
1.3 組件化的優勢
組件化基于可重用的目的,將應用拆分成多個獨立組件,以減少耦合:
加快編譯速度:每個業務功能都是一個單獨的工程,可獨立編譯運行,拆分后代碼量較少,編譯自然變快。
解耦:通過關注點分離的形式,將App分離成多個模塊,每個模塊都是一個組件。
提高開發效率:多人開發中,每個組件模塊由單人負責,降低了開發之間溝通的成本,減少因代碼風格不一而產生的相互影響。
代碼復用:類似我們引用的第三方庫,可以將基礎組件或功能組件剝離。在新項目微調或直接使用。
1.4 組件化需要解決的問題
組件分層:怎么將一個項目分成多個組件、組件間的依賴關系是怎么樣的?
組件單獨運行和集成調試:組件是如何獨立運行和集成調試的?
組件間通信:主項目與組件、組件與組件之間如何通信就變成關鍵?
二、組件分層
組件依賴關系是上層依賴下層,修改頻率是上層高于下層。先上一張圖:
2.1 基礎組件
基礎公共模塊,最底層的庫:
封裝公用的基礎組件;
網絡訪問框架、圖片加載框架等主流的第三方庫;
各種第三方SDK。
2.2 common組件(lib_common)
支撐業務組件、功能組件的基礎(BaseActivity/BaseFragment等基礎能力;
依賴基礎組件層;
業務組件、功能組件所需的基礎能力只需要依賴common組件即可獲得。
2.3 功能組件
依賴基礎組件層;
對一些公用的功能業務進行封裝與實現;
業務組件可以在library和application之間切換,但是最后打包時必須是library ;
2.4 業務組件
可直接依賴基礎組件層;同時也能依賴公用的一些功能組件;
各組件之間不存在依賴關系,通過路由進行通信;
業務組件可以在library和application之間切換,但是最后打包時必須是library ;
2.5 主工程(app)
只依賴各業務組件;
除了一些全局的配置和主Activity之外,不包含任何業務代碼,是應用的入口;
2.6 完成后項目
這只是個大概,并不是說必須這樣,可以按照自己的方式來。比如:你覺得基礎組件比較多導致project里面的項目太多,那么你可以創建一個lib_base,然在lib_base里面再創建其他基礎組件即可。
三、組件單獨調試
3.1 創建組件()
library和application之間切換:選擇第一項。
始終是library:選擇第二項
這樣盡可能的減少變動項,當然這僅僅是個建議,看個人習慣吧。
因為咱們創建的是一個module,所以在AndridManifest中添加android:exported="true"屬性可直接構建一個APK。下面咱們看看如何生成不同的工程類型。
3.2 動態配置組件的工程類型
在 AndroidStudio 開發 Android 項目時,使用的是 Gradle 來構建,具體來說使用的是 Android Gradle 插件來構建,Android Gradle 中提供了三種插件,在開發中可以通過配置不同的插件來構建不同的工程。
3.2.1 build.gradle(module)
//構建后輸出一個?APK?安裝包
apply?plugin:?'com.android.application'
//構建后輸出?ARR?包
apply?plugin:?'com.android.library'
//配置一個?Android?Test?工程
apply?plugin:?'com.android.test'
獨立調試:設置為 Application 插件。
集成調試:設置為 Library 插件。
3.2.2 設置gradle.properties
isDebug = true 獨立調試
3.2.3 動態配制插件(build.gradle)
//注意gradle.properties中的數據類型都是String類型,使用其他數據類型需要自行轉換
if(isDebug.toBoolean()){
//構建后輸出一個?APK?安裝包
apply?plugin:?'com.android.application'
}else{
//構建后輸出?ARR?包
apply?plugin:?'com.android.library'
}
3.3 動態配置組件的 ApplicationId 和 AndroidManifest 文件
一個 APP 是只有一個 ApplicationId ,所以在單獨調試和集成調試組件的 ApplicationId 應該是不同的。
單獨調試時也是需要有一個啟動頁,當集成調試時主工程和組件的AndroidManifest文件合并會產生多個啟動頁。
根據上面動態配制插件的經驗,我們也需要在build.gradle中動態配制ApplicationId 和 AndroidManifest 文件。
3.3.1 準備兩個不同路徑的 AndroidManifest 文件
有什么不同?咱們一起看看具體內容。
3.3.2 src/main/debug/AndroidManifest
package="com.scc.module.collect"> android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.SccMall"> android:exported="true">
3.3.3 src/main/debug/AndroidManifest
package="com.scc.module.collect"> android:allowBackup="true" android:supportsRtl="true" >
3.3.4 動態配制(build.gradle)
defaultConfig?{
if(isDebug.toBoolean()){
//獨立調試的時候才能設置applicationId
applicationId?"com.scc.module.collect"
}
}
sourceSets?{
main?{
if?(isDebug.toBoolean())?{
//獨立調試
manifest.srcFile?'src/main/debug/AndroidManifest.xml'
}?else?{
//集成調試
manifest.srcFile?'src/main/AndroidManifest.xml'
}
}
}
3.4 實現效果
3.4.1 獨立調試
isDebug = true
3.4.2 集成調試
isDebug = false
四、Gradle配置統一管理
4.1 config.gradle
當我們需要進行插件版本、依賴庫版本升級時,項目多的話改起來很麻煩,這時就需要我們對Gradle配置統一管理。如下:
具體內容
ext{
//組件獨立調試開關,?每次更改值后要同步工程
isDebug?=?true
android?=?[
//?編譯?SDK?版本
compileSdkVersion:?31,
//?最低兼容?Android?版本
minSdkVersion????:?21,
//?最高兼容?Android?版本
targetSdkVersion?:?31,
//?當前版本編號
versionCode??????:?1,
//?當前版本信息
versionName??????:?"1.0.0"
]
applicationid?=?[
app:"com.scc.sccmall",
main:"com.scc.module.main",
webview:"com.scc.module.webview",
login:"com.scc.module.login",
collect:"com.scc.module.collect"
]
dependencies?=?[
"appcompat"?????????:'androidx.appcompat:appcompat:1.2.0',
"material"??????????:'com.google.android.material:material:1.3.0',
"constraintlayout"??:'androidx.constraintlayout:constraintlayout:2.0.1',
"livedata"??????????:'androidx.lifecycle:lifecycle-livedata:2.4.0',
"viewmodel"?????????:'androidx.lifecycle:lifecycle-viewmodel:2.4.0',
"legacyv4"??????????:'androidx.legacy:legacy-support-v4:1.0.0',
"splashscreen"??????:'androidx.core:core-splashscreen:1.0.0-alpha01'
]
libARouter=?'com.alibaba:arouter-api:1.5.2'
libARouterCompiler?=?'com.alibaba:arouter-compiler:1.5.2'
libGson?=?'com.google.code.gson:gson:2.8.9'
}
4.2 添加配制文件build.gradle(project)
apply?from:"config.gradle"
4.3 其他組件使用
//build.gradle
//注意gradle.properties中的數據類型都是String類型,使用其他數據類型需要自行轉換
if(isDebug.toBoolean()){
//構建后輸出一個?APK?安裝包
apply?plugin:?'com.android.application'
}else{
//構建后輸出?ARR?包
apply?plugin:?'com.android.library'
}
android?{
compileSdkVersion?31
defaultConfig?{
if(isDebug.toBoolean()){
//獨立調試的時候才能設置applicationId
applicationId?"com.scc.module.collect"
}
minSdkVersion?21
targetSdkVersion?31
versionCode?1
versionName?"1.0"
testInstrumentationRunner?"androidx.test.runner.AndroidJUnitRunner"
}
buildTypes?{
release?{
minifyEnabled?false
proguardFiles?getDefaultProguardFile('proguard-android-optimize.txt'),?'proguard-rules.pro'
}
}
sourceSets?{
main?{
if?(isDebug.toBoolean())?{
//獨立調試
manifest.srcFile?'src/main/debug/AndroidManifest.xml'
}?else?{
//集成調試
manifest.srcFile?'src/main/AndroidManifest.xml'
}
}
}
compileOptions?{
sourceCompatibility?JavaVersion.VERSION_1_8
targetCompatibility?JavaVersion.VERSION_1_8
}
}
dependencies?{
//????implementation?root.dependencies.appcompat
//????implementation?root.dependencies.material
//????implementation?root.dependencies.constraintlayout
//????implementation?root.dependencies.livedata
//????implementation?root.dependencies.viewmodel
//????implementation?root.dependencies.legacyv4
//????implementation?root.dependencies.splashscreen
//????implementation?root.libARouter
//上面內容在lib_common中已經添加咱們直接依賴lib_common
implementation?project(':lib_common')
testImplementation?'junit:junit:4.+'
androidTestImplementation?'androidx.test.ext:junit:1.1.2'
androidTestImplementation?'androidx.test.espresso:espresso-core:3.3.0'
}
五、組件間界面跳轉(ARouter)
5.1 介紹
Android 中的界面跳轉那是相當簡單,但是在組件化開發中,由于不同組件式沒有相互依賴的,所以不可以直接訪問彼此的類,這時候就沒辦法通過顯式的方式實現了。
所以在這里咱們采取更加靈活的一種方式,使用 Alibaba 開源的 ARouter 來實現。
一個用于幫助 Android App 進行組件化改造的框架 —— 支持模塊間的路由、通信、解耦
文檔介紹的蠻詳細的,感興趣的可以自己實踐一下。這里做個簡單的使用。
5.2 使用
5.2.1 添加依賴
先在統一的config.gradle添加版本等信息
ext{
...
libARouter=?'com.alibaba:arouter-api:1.5.2'
libARouterCompiler?=?'com.alibaba:arouter-compiler:1.5.2'
}
因為所有的功能組件和業務組件都依賴lib_common,那么咱們先從lib_common開始配制
lib_common
dependencies?{
api?root.libARouter
...
}
其他組件(如collect)
android?{
defaultConfig?{
...
javaCompileOptions?{
annotationProcessorOptions?{
arguments?=?[AROUTER_MODULE_NAME:?project.getName()]
//如果項目內有多個annotationProcessor,則修改為以下設置
//arguments?+=?[AROUTER_MODULE_NAME:?project.getName()]
}
}
}
}
dependencies?{
//arouter-compiler的注解依賴需要所有使用?ARouter?的?module?都添加依賴
annotationProcessor?root.libARouterCompiler
...
}
5.2.2 添加注解
你要跳轉的Activity
//?在支持路由的頁面上添加注解(必選)
//?這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path?=?"/collect/CollectActivity")
public?class?CollectActivity?extends?AppCompatActivity?{
@Override
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collect);
}
}
5.2.3 初始化SDK(主項目Application)
public?class?App?extends?BaseApplication?{
@Override
public?void?onCreate()?{
super.onCreate();
if?(isDebug())?{???????????//?這兩行必須寫在init之前,否則這些配置在init過程中將無效
ARouter.openLog();?????//?打印日志
ARouter.openDebug();???//?開啟調試模式(如果在InstantRun模式下運行,必須開啟調試模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(this);?//?盡可能早,推薦在Application中初始化
}
private?boolean?isDebug()?{
return?BuildConfig.DEBUG;
}
}
5.3 發起路由操作
5.3.1 應用內簡單的跳轉
ARouter.getInstance().build("/collect/CollectActivity").navigation();
這里是用module_main的HomeFragment跳轉至module_collect的CollectActivity界面,兩個module中不存在依賴關系。"/collect/CollectActivity"在上面已注冊就不多描述了。
效果如下:
5.3.2 跳轉并攜帶參數
這里是用module_main的MineFragment的Adapter跳轉至module_webview的WebViewActivity界面,兩個module中同樣不存在依賴關系。
啟動方
ARouter.getInstance().build("/webview/WebViewActivity")
.withString("url",?bean.getUrl())
.withString("content",bean.getName())
.navigation();
這里傳了兩個參數url和name到WebViewActivity,下面咱們看看WebViewActivity怎么接收。
接收方
//為每一個參數聲明一個字段,并使用?@Autowired?標注
//URL中不能傳遞Parcelable類型數據,通過ARouter?api可以傳遞Parcelable對象
//添加注解(必選)
@Route(path?=?"/webview/WebViewActivity")
public?class?WebViewActivity?extends?BaseActivity
//發送方和接收方定義的key名稱相同則無需處理
@Autowired
public?String?url;
//通過name來映射URL中的不同參數
//發送方定義key為content,我們用title來接收
@Autowired(name?=?"content")
public?String?title;
@Override
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
//注入參數和服務(這里用到@Autowired所以要設置)
//不使用自動注入,可不寫,如CollectActivity沒接收參數就沒有設置
ARouter.getInstance().inject(this);
binding.btnBoom.setText(String.format("%s,你來啦",?title));
//加載鏈接
initWebView(binding.wbAbout,?url);
}
}
上效果圖:
搞定,更多高級玩法可自行探索。
5.3.3 小記(ARouter目標不存在)
W/ARouter::: ARouter::There is no route match the path
這里出現個小問題,配置注釋都好好的,但是發送發無論如何都找不到設置好的Activity。嘗試方案:
Clean Project
Rebuild Project
在下圖也能找到ARouter內容。
后來修改Activity名稱好了。
六、組件間通信(數據傳遞)
界面跳轉搞定了,那么數據傳遞怎么辦,我在module_main中使用懸浮窗,但是需要判斷這個用戶是否已登錄,再執行后續邏輯,這個要怎么辦?這里我們可以采用 接口 + ARouter 的方式來解決。
在這里可以添加一個 componentbase 模塊,這個模塊被所有的組件依賴。
這里我們通過 module_main組件 中調用 module_login組件 中的方法來獲取登錄狀態這個場景來演示。
6.1 通過依賴注入解耦:服務管理(一) 暴露服務
6.1.1 創建 componentbase 模塊(lib)
6.1.2 創建接口并繼承IProvider
注意:接口必須繼承IProvider,是為了使用ARouter的實現注入。
6.1.3 在module_login組件中實現接口
lib_common
所有業務組件和功能組件都依賴lib_common,所以咱們直接在lib_common添加依賴即可
dependencies?{
...
api?project(":lib_componentbase")
}
module_login
dependencies?{
...
implementation?project(':lib_common')
}
實現接口
//實現接口
@Route(path?=?"/login/AccountServiceImpl")
public?class?AccountServiceImpl?implements?IAccountService?{
@Override
public?boolean?isLogin()?{
MLog.e("AccountServiceImpl.isLogin");
return?true;
}
@Override
public?String?getAccountId()?{
MLog.e("AccountServiceImpl.getAccountId");
return?"1000";
}
@Override
public?void?init(Context?context)?{
}
}
6.2 通過依賴注入解耦:服務管理(二) 發現服務
6.2.1 在module_main中調用調用是否已登入
public?class?HomeFragment?extends?BaseFragment
@Autowired
IAccountService?accountService;
@Override
public?void?onViewCreated(@NonNull?@NotNull?View?view,?@Nullable?@org.jetbrains.annotations.Nullable?Bundle?savedInstanceState)?{
super.onViewCreated(view,?savedInstanceState);
ARouter.getInstance().inject(this);
binding.frgmentHomeFab.setOnClickListener(new?View.OnClickListener()?{
@Override
public?void?onClick(View?v)?{
MLog.e("Login:"+accountService.isLogin());
MLog.e("AccountId:"+accountService.getAccountId());
}
});
}
}
運行結果:
E/-SCC-:?AccountServiceImpl.isLogin
E/-SCC-:?Login:true
E/-SCC-:?AccountServiceImpl.getAccountId
E/-SCC-:?AccountId:1000
七、總結
本文介紹了組件化、組件分層、解決了組件的獨立調試、集成調試、頁面跳轉、組件通信等。
其實會了這些后你基本可以搭建自己的組件化項目了。其實最大的問題還是分組分層、組件劃分。這個就需要根據你的實際情況來設置。
本項目比較糙,后面會慢慢完善。比如添加Gilde、添加MMVK、添加Room等。
項目傳送門https://github.com/shuaici/SccMall
相關推薦
Android OkHttp+Retrofit+Rxjava+Hilt實現網絡請求框架
參考與感謝
“終于懂了” 系列:Android組件化,全面掌握!
Android 組件化最佳實踐
手把手帶你搭建一個優秀的Android項目架構
Android Gradle
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。