MVVM+组件化+Arouter实现

编程入门 行业动态 更新时间:2024-10-06 22:25:31

MVVM+<a href=https://www.elefans.com/category/jswz/34/1771375.html style=组件化+Arouter实现"/>

MVVM+组件化+Arouter实现

code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群

作者:孤縌
链接:
声明:本文已获孤縌授权发表,转发等请联系原作者授权

MVVM+组件化实现

模块概览

底层模块:common、network、resource

功能模块:player、firebase、pay

界面模块:mobile、tablet、tablet_login

壳模块: app

模块实现

1.公共模块的gradle配置

由于不同模块,可能引用相同的依赖库,那么对于这部分共同的模块则需要提取出来做统一的管理,因此在项目的根目录创建了common.gradle。

//project是根项目,可以简单认为是个对象,对象持有一个名词为ext的成员变量
project.ext {//基本的变量配合和版本控制compileSdkVersion = 30buildToolsVersion = "30"minSdkVersion = 21targetSdkVersion = 30applicationId = "com.sdmc.xmediatv"versionCode = 4versionName = "4.7.0"//设置app配置setAppDefaultConfig = {extension ->//指定为application,代表该模块可以单独调试extension.apply plugin: 'com.android.application'extension.description "app"//公共的apply 主要是用于三方库extension.apply plugin: 'kotlin-android'extension.apply plugin: 'kotlin-android-extensions'extension.apply plugin: 'kotlin-kapt'//设置项目的androidsetAppAndroidConfig extension.android//设置项目的三方库依赖setDependencies extension.dependencies}//设置lib配置(只可以作为lib,不可单独调试)setLibDefaultConfig = {extension ->//library,代表只是单纯的库,不需要依赖其他模块extension.apply plugin: 'com.android.library'extension.description "lib"setLibAndroidConfig extension.androidsetDependencies extension.dependencies}//是否允许module单独调试isModuleDebug = false//动态改变,用于单模块调试setAppOrLibDefaultConfig = {extension ->if (project.ext.isModuleDebug) {extension.apply plugin: 'com.android.application'extension.description "app"} else {extension.apply plugin: 'com.android.library'extension.description "lib"}extension.apply plugin: 'kotlin-android'extension.apply plugin: 'kotlin-android-extensions'extension.apply plugin: 'kotlin-kapt'//设置通用Android配置setAppOrLibAndroidConfig extension.android//设置通用依赖配置setDependencies extension.dependencies}//设置application 公共的android配置setAppAndroidConfig = {extension -> //extension 相当于 android 对象extensionpileSdkVersion project.extpileSdkVersionextension.buildToolsVersion project.ext.buildToolsVersionextension.defaultConfig {applicationId project.ext.applicationIdminSdkVersion project.ext.minSdkVersiontargetSdkVersion project.ext.targetSdkVersionversionCode project.ext.versionCodeversionName project.ext.versionNameextension.flavorDimensions "versionCode"testInstrumentationRunner "android.support.tablet_test.runner.AndroidJUnitRunner"//ARouter 编译生成路由kapt {arguments {arg("AROUTER_MODULE_NAME", project.getName())includeCompileClasspath = true}}}extension.buildFeatures {dataBinding = true// for view binding :// viewBinding = true}}//设置lib 公共的android配置setLibAndroidConfig = {extension -> //extension 相当于 android 对象extensionpileSdkVersion project.extpileSdkVersionextension.buildToolsVersion project.ext.buildToolsVersionextension.defaultConfig {minSdkVersion project.ext.minSdkVersiontargetSdkVersion project.ext.targetSdkVersionversionCode project.ext.versionCodeversionName project.ext.versionNametestInstrumentationRunner "android.support.tablet_test.runner.AndroidJUnitRunner"//ARouter 编译生成路由/*kapt {arguments {arg("AROUTER_MODULE_NAME", project.getName())}}*/}extension.buildFeatures {dataBinding = true// for view binding :// viewBinding = true}}//设置通用的 android配置(可作为project单独调试)setAppOrLibAndroidConfig = {extension -> //extension 相当于 android 对象extensionpileSdkVersion project.extpileSdkVersionextension.buildToolsVersion project.ext.buildToolsVersionextension.defaultConfig {minSdkVersion project.ext.minSdkVersiontargetSdkVersion project.ext.targetSdkVersionversionCode project.ext.versionCodeversionName project.ext.versionNameextension.flavorDimensions "versionCode"testInstrumentationRunner "android.support.tablet_test.runner.AndroidJUnitRunner"//ARouter 编译生成路由kapt {arguments {arg("AROUTER_MODULE_NAME", project.getName())includeCompileClasspath = true}}}extension.buildFeatures {//启用自动绑定view iddataBinding = true}//使用的jdk版本extensionpileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}extension.kotlinOptions {jvmTarget = JavaVersion.VERSION_1_8}//动态改变清单文件资源指向extension.sourceSets {main {if (project.ext.isModuleDebug) {manifest.srcFile 'src/main/manifest/AndroidManifest.xml'} else {manifest.srcFile 'src/main/AndroidManifest.xml'}}}}//公用的三方库依赖,慎重引入,主要引入基础库依赖setDependencies = {extension ->extension.implementation fileTree(dir: 'libs', include: ['*.jar'])extension.kapt 'com.alibaba:arouter-compiler:1.2.2'//ARouter 路由apt插件,用于生成相应代码,每个module都需要extension.implementation 'com.alibaba:arouter-api:1.5.0'extension.implementation 'com.alibaba:arouter-compiler:1.2.2'extension.implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"extension.implementation 'androidx.core:core-ktx:1.3.1'extension.implementation 'com.google.code.gson:gson:2.8.6'extension.implementation 'androidx.appcompat:appcompat:1.2.0'extension.implementation 'androidx.constraintlayout:constraintlayout:2.0.0'extension.implementation 'androidx.recyclerview:recyclerview:1.0.0'}
}

根目录的build.gradle:

buildscript {//统一制定版本ext.kotlin_version = '1.4.31'ext.google_service_version = '19.7.0'ext.exoplayer_version = '2.13.2'repositories {//为当前的build.gradle设置依赖库中心google()jcenter()}dependencies {//gradle版本classpath "com.android.tools.build:gradle:4.0.1"//kotlin依赖classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"//bintray maven的依赖classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"//sonatype maven的依赖classpath "com.github.dcendents:android-maven-gradle-plugin:1.5"//黄油刀classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'//谷歌服务classpath 'com.google.gms:google-services:4.3.3'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}allprojects {repositories {//jitpack库maven { url '' }//bintray维护的Maven仓库jcenter()//谷歌maven库maven { url "" }google()//阿里云镜像maven库地址maven {url ""}}
}task clean(type: Delete) {delete rootProject.buildDir
}
2.壳app模块的实现
//从项目的根目录中找到common.gradle
apply from: "${rootProject.rootDir}/common.gradle"//project.ext则是之前强调的common.gradle中的对象,该对象有setAppDefaultConfig
project.ext.setAppDefaultConfig project//由于setAppDefaultConfig中的android配置是通用配置,所以还需要针对壳本身新增配置
android {//混淆规则配置buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}debug {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}packagingOptions {//nanohttpd轻量级数据库所需exclude 'META-INF/nanohttpd/default-mimetypes.properties'exclude 'META-INF/nanohttpd/mimetypes.properties'//为了解决部分第三方库重复打包了META-INF的问题exclude 'META-INF/NOTICE'exclude 'META-INF/LICENSE'//解决kotlin问题exclude("META-INF/*.kotlin_module")}compileOptions {sourceCompatibility = '1.8'targetCompatibility = '1.8'}kotlinOptions {jvmTarget = "1.8"}//在本地loacl.propreties中配置打包所需信息Properties properties = new Properties()InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream()properties.load(inputStream)def sdkDir = properties.getProperty('key.file')def keyFile = file(sdkDir)def key_keyAlias = properties.getProperty('keyAlias')def key_keyPassword = properties.getProperty('keyPassword')def key_storePassword = properties.getProperty('storePassword')signingConfigs {release {v2SigningEnabled true}debug {storeFile file(keyFile)storePassword key_storePasswordkeyAlias key_keyAliaskeyPassword key_keyPassword}}/*android.applicationVariants.all {variant ->variant.outputs.all {output ->outputFileName = new File("../version/", "XMediaTV_Android_" +defaultConfig.versionName + "." + releaseTime() + "_" + variant.buildType.name + ".apk")}}*///android打包提示check release builds falselintOptions {checkReleaseBuilds falseabortOnError false}
}dependencies {//壳工程所依赖的模块,implementation代表不依赖模块下的三分库implementation project(path: ':mobile')implementation project(path: ':tablet')//api 代表依赖模块下所有的库api project(path: ':common')api project(path: ':firebase')
}
//设置仓库地址,用于辅助三方库依赖文件下载
repositories {mavenCentral()
}
3.底层模块的model配置

common中的gradle文件:

common模块本身就是为了通用,那么对于一些常用的库,则需要在该模块做一定的依赖处理,以下对不同模块功能依赖做出了标注

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'android {compileSdkVersion 29buildToolsVersion "29.0.3"defaultConfig {minSdkVersion 21targetSdkVersion 29versionCode 1versionName "1.0.0"//设置预览界面的库支持vectorDrawables.useSupportLibrary = truetestInstrumentationRunner "androidx.tablet_test.runner.AndroidJUnitRunner"//混淆文件指定consumerProguardFiles "consumer-rules.pro"//配合根目录的gradle.properties,代码中调用BuildConfig.SERVER_ADDRESS动态获取对应值,用于自动打包改服务地址buildConfigField "String", "SERVER_ADDRESS", "\"${SERVER_ADDRESS}\""buildConfigField "String", "TENANT_CODE", "\"${TENANT_CODE}\""buildConfigField "String", "AD_SERVER_ADDRESS", "\"${AD_SERVER_ADDRESS}\""buildConfigField "String", "PLAYER_SERVER_URI", "\"${PLAYER_SERVER_URI}\""buildConfigField "Boolean", "FORCE_USE_TABLET", project.properties.get("FORCE_USE_TABLET")}//设置arouter模块名称kapt {arguments {arg("AROUTER_MODULE_NAME", project.getName())}}//资源前缀强制命名resourcePrefix "common_"//混淆模式buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}//用于换肤,让所有model都可以拥有不同类型的资源目录sourceSets {main {res.srcDirs = ['src/main/res','src/main/res-light','src/main/res-news']}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}
}dependencies {implementation fileTree(dir: "libs", include: ["*.jar"])//》》》》kotlin//kotlin核心库配置,对应根目录的build.gradle版本implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.core:core-ktx:1.3.2'//kotlin协程库api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt'api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3-native-mt'//》》》》视图//android视图库api 'androidx.appcompat:appcompat:1.2.0'api 'com.google.android.material:material:1.3.0'api 'androidx.constraintlayout:constraintlayout:2.0.1'api 'androidx.recyclerview:recyclerview:1.1.0'//glide图片加载库api 'com.github.bumptech.glide:glide:4.12.0'//圆角的ImageView库api 'com.makeramen:roundedimageview:2.3.0'//recycleView的adapter库api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'//下拉刷新库implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'//头条的自适应库api 'me.jessyan:autosize:1.2.1'//换肤库api 'skin.support:skin-support:4.0.5'                   // skin-supportapi 'skin.support:skin-support-appcompat:4.0.5'         // skin-support 基础控件支持api 'skin.support:skin-support-design:4.0.5'// skin-support-design material design 控件支持[可选]api 'skin.support:skin-support-cardview:4.0.5'// skin-support-cardview CardView 控件支持[可选]api 'skin.support:skin-support-constraint-layout:4.0.5'// skin-support-constraint-layout ConstraintLayout 控件支持[可选]//本地资源库api project(path: ':resource')//》》》》网络//用于retrofit2库依赖implementation 'com.squareup.retrofit2:converter-gson:2.9.0'implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'//用于retrofit2+kotlin+coroutines的回调库,可以直接加上suspend关键字获取到回调对象implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'//okhttp3库implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2'//gson解析库implementation 'com.google.code.gson:gson:2.8.6'//》》》》其余基本库//郭霖的Litepal数据库api 'org.litepal.guolindev:core:3.2.2'//sharePreference库api 'com.orhanobut:hawk:2.0.1'//阿里的路由库kapt 'com.alibaba:arouter-compiler:1.2.2'api 'com.alibaba:arouter-api:1.5.0'//谷歌的deeplink分享implementation 'com.google.firebase:firebase-dynamic-links:19.1.0'//--------------------------------------------------------------//》》》》lifecycle库,用于liveData和viewModle,使用与mvvm框架def lifecycle_version = '2.2.0'// ViewModel and LiveDataapi "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"// alternatively - just ViewModel
//    api "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'// For Kotlin use lifecycle-viewmodel-ktx// alternatively - just LiveData
//    api "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"// 有生命周期感知的协程api 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'}

network中的gradle文件:

该模块为网络请求模块,用到的框架是retrofit2 + kotlin + coroutines,本质是请求耗时网络,通过kotlin使用coroutines的suspend挂起,来达到子线程请求,主线程接收数据的效果。

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {compileSdkVersion 29defaultConfig {minSdkVersion 21targetSdkVersion 29versionCode 1versionName "1.0.0"buildConfigField "String", "SERVER_ADDRESS", "\"${SERVER_ADDRESS}\""buildConfigField "String", "TENANT_CODE", "\"${TENANT_CODE}\""buildConfigField "String", "AD_SERVER_ADDRESS", "\"${AD_SERVER_ADDRESS}\""buildConfigField "String", "PLAYER_SERVER_URI", "\"${PLAYER_SERVER_URI}\""buildConfigField "Boolean", "FORCE_USE_TABLET", project.properties.get("FORCE_USE_TABLET")}resourcePrefix "network_"buildTypes {debug {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}release {minifyEnabled true}}compileOptions {sourceCompatibility = '1.8'targetCompatibility = '1.8'}}dependencies {implementation fileTree(dir: "libs", include: ["*.jar"])//用于添加retrofit2拦截请求头用的implementation 'com.squareup.okhttp3:logging-interceptor:4.8.1'//Chuck辅助工具,用于打印各种http请求debugImplementation 'com.readystatesoftware.chuck:library:1.1.0'releaseImplementation 'com.readystatesoftware.chuck:library-no-op:1.1.0'//--------------------------------------------------------------api project(path: ':common')implementation files('libs\\XMediaCryptoJar_HKSC.jar')
}

resoure的gradle:

该Model主要是用于存放抽取的颜色、多语言、以及与app相关的图标、占位图等

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'android {compileSdkVersion 29buildToolsVersion "29.0.3"defaultConfig {minSdkVersion 21targetSdkVersion 29versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"consumerProguardFiles "consumer-rules.pro"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}sourceSets {main {res.srcDirs = ['src/main/res','src/main/res-light','src/main/res-news']}}
}dependencies {implementation fileTree(dir: "libs", include: ["*.jar"])//kotlinimplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.core:core-ktx:1.3.2'//单元测试库testImplementation 'junit:junit:4.13.2'androidTestImplementation 'androidx.test.ext:junit:1.1.2'androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'}

基本模块配置都是作为library库使用,对于基本模块的内容有要求,就是一定是一项基础并且多模块共用的功能,才值得放在基础模块中。

4.功能模块或界面模块的依赖配置

对于模块依赖,只要弄清楚api和implementation的功能,并且清楚common.gradle,是如何使用的,就能很清晰的使用模块化项目开发。

apply from: "${rootProject.rootDir}/common.gradle"//使用common.gradle中的公用配置
project.ext.setAppOrLibDefaultConfig projectandroid {resourcePrefix "tablet_"buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}}dependencies {//这里统一依赖了network,network中由于是api common,common api resource,所以只需要implementation network就行implementation project(path: ':network')//统一依赖功能model,播放器、Firebase、支付implementation project(path: ':player')implementation project(path: ':firebase')implementation project(path: ':pay')//依赖了一个界面模块,含各种平板的弹窗界面和登录implementation project(path: ':tablet_login')//下拉刷新库implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'//轮播图库implementation 'com.github.zhpanvip:BannerViewPager:3.1.6'//谷歌支付库implementation 'com.android.billingclient:billing:2.1.0'//依次是,谷歌消息、DeepLink、广告implementation 'com.google.firebase:firebase-messaging:20.1.2'implementation 'com.google.firebase:firebase-dynamic-links:19.1.0'implementation "com.google.android.gms:play-services-ads:$google_service_version"
}

模块内容

1mon

base包:

存放着各种基类,其中主要的是MVVM,所以着重讲述MVVM框架搭建

ViewModel讲解:

首先,要继承ViewModel,导包,viewModel主要功能是,代替UI界面去做耗时操作,所以通过viewModelScope对象调用协程挂起,最终通过liveData绑定数据回调。

api "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" //viewModel+livedata
api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0' //kotlin专用
由于ViewModel是配合Coroutine使用的,所以必需先认识CoroutineScope(协程范围)和withContext(Dispatchers.IO)中的Dispatchers模式,这里先讲Dispatchers的用途和具体场景。
//实现LifecycleObserver接口,是为了通过activity的lifecycle.addObserver(LifecycleObserver) 绑定到生命周期中
open class BaseViewModel : ViewModel(), LifecycleObserver {//viewModelScope是ViewModel中的成员对象,该对象实现了CoroutineScope接口//suspend CoroutineScope.() -> Unit, suspend是kotlin线程挂起的标识,实际上去走了await()方法 //CoroutineScope.() -> Unit 代表一个方法体,并且该方法体对象是CoroutineScope接口对象,主要是规范方法体类型,可除去CoroutineScope.fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch {try {block()} catch (e: Exception) {e.printStackTrace()}}//相比上个方法,主要是多了 withContext(Dispatchers.IO),目的是切换到子线程做耗时操作fun launchIO(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch {withContext(Dispatchers.IO) {try {block()} catch (e: Exception) {e.printStackTrace()}}}//errorHandler 是为了统一处理网络异常fun launchIO(block: suspend CoroutineScope.() -> Unit,errorHandler: (code: Int, message: String) -> Unit?,) = viewModelScope.launch {withContext(Dispatchers.IO) {try {if (NetWorkUtils.isNetConnect(CommonManager.context)) {block()} else {errorHandler(300, "No internet connection")}} catch (e: Exception) {handlerErrorCode(e, errorHandler)e.printStackTrace()}finally {//....}}}private fun handlerErrorCode(e: Exception,errorHandler: (code: Int, message: String) -> Unit?,) {when (e) {is HttpException -> {errorHandler(e.code(), e.message())}is UnknownHostException -> {errorHandler(404, "Unable to connect to server")}is SocketTimeoutException -> {errorHandler(408, "Socket time out")//访问超时}is ConnectException -> {errorHandler(404, "Connect exception")}is SocketException -> {errorHandler(500, "Socket exception")}is EOFException -> {errorHandler(500, "EOF exception") //连接意外中断}is IllegalArgumentException -> {errorHandler(400, "Illegal argument exception")//参数错误}is SSLException -> {errorHandler(401, "SSL exception")//证书错误}is NullPointerException -> {errorHandler(600, "Null pointer exception")}else -> {errorHandler(700, "Unknown exception")}}}fun Map<String, Any>.getBody(): RequestBody {return Gson().toJson(this).toRequestBody("application/json".toMediaTypeOrNull())}data class ErrorHandler(val code: Int, val message: String)
}

BaseVMActivity讲解:

//持有俩个泛型,VM和DB,分别是BaseViewModel和ViewDataBinding的实现类
abstract class BaseVMActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseActivity(){//是否打印生命周期var openLifecycle: Boolean = falselateinit var viewModel: VMlateinit var dataBinding: DBoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)LogUtil.e(TAG, "onCreate", openLifecycle)//绑定视图,获取到dataBindingdataBinding = DataBindingUtil.setContentView(this, provideContentViewId())tvTitle = dataBinding.root.findViewById(R.id.title)back = dataBinding.root.findViewById(R.id.back)//实例化ViewModelviewModel = initVM()//让viewModel关联生命周期lifecycle.addObserver(viewModel)//开始接口监听回调startObserve()}abstract fun initVM(): VMabstract fun startObserve()override fun onResume() {super.onResume()LogUtil.e(TAG, "onResume", openLifecycle)}override fun onPause() {super.onPause()LogUtil.e(TAG, "onPause", openLifecycle)}override fun onStart() {super.onStart()LogUtil.e(TAG, "onStart", openLifecycle)}override fun onRestart() {super.onRestart()LogUtil.e(TAG, "onRestart", openLifecycle)}override fun onStop() {super.onStop()LogUtil.e(TAG, "onStop", openLifecycle)}override fun onDestroy() {super.onDestroy()LogUtil.e(TAG, "onDestroy", openLifecycle)}//Arouter路由跳转封装, action代表Postcard方法体回调fun open(path: String, requestCode: Int = 0, action: Postcard.() -> Unit = {}) {val postcard = ARouter.getInstance().build(path)postcard.action()postcard.navigation(this, requestCode)}fun openWithFinish(path: String, action: Postcard.() -> Unit = {}) {open(path, 0, action)finish()}
}

router包:

路由本意是,为了解决不同模块之间无法访问各自界面问题

路由指定,首先需要定义路径名,然后在对应的类中新增注解@Route(path = RouterPath.MOBILE_HOME),之后通过Arouter的navigation()方法跳转到不同模块的界面,从而实现跨模块的跳转。

interface RouterPath {companion object {//mobileconst val MOBILE_HOME = "/mobile/home_activity"const val MOBILE_SPLASH = "/mobile/splash"const val MOBILE_VOD_DETAIL = "/mobile/vod_detail"const val MOBILE_LIVE = "/mobile/live"const val MOBILE_PAY = "/mobile/pay/activity"const val MOBILE_EVENT = "/mobile/event"//tabletconst val TABLET_HOME = "/tablet/home_activity"const val TABLET_VOD_DETAIL = "/tablet/vod_detail"const val TABLET_SPLASH = "/tablet/splash"const val TABLET_EVENT = "/tablet/event"const val PAY_ACTIVITY = "/pay/activity"}
}

view包:

adapterItemDecoration是存放recycleview的装饰类ItemDecoration

viewExpand是存放所有视图相关的kotlin方法扩展,例如ImageView的加载图片封装

其余的则为view包中的一些常用的自定义view视图

2work:

在了解network模块之前,必需对retrofit的使用由清晰的认识,所以接下来就看一个正常的retrofit是如何调用的。

调用库的依赖:

//retrofit核心库,这个在我们的common包中就有依赖
api 'com.squareup.retrofit2:retrofit:2.9.0'
//加上这个之后,你可以省略输入流转Gson的步骤
api 'com.squareup.retrofit2:converter-gson:2.9.0'
//使用之后,支持接口回调返回Deferred<T>对象,该对象是延迟结果发送的意思,可以使用suspend代替
api 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

实现步骤:

//1.定义接口,并申明请求方式和对应的返回结果
interface TestApi {//get请求,(/{body})@GET("/test/{body}")fun getTest(@Path("test") body: String): Deferred<Test>
}//2.使用retrofit初始化TestApi接口的实现对象
val myTestApi by lazy {val retrofit = retrofit2.Retrofit.Builder().baseUrl("")//添加Gson转换.addConverterFactory(GsonConverterFactory.create())//添加Deferred转换.addCallAdapterFactory(CoroutineCallAdapterFactory()).build()//创建实现TestApi接口的MyTestApi对象,通过该对象就可以调用getTest()方法去走对应的网络请求retrofit.create(MyTestApi::class.java)
}//3.使用协程实现简洁回调
//确保在UI线程中调用,当getTest方法执行后,回调用await()挂起,如果在方法中申明suspend,则可以省略,直接让getTest()返回Test
GlobalScope.launch(Dispatchers.Main) {try {//回调UI线程中刷新界面数据onResponseUI(myTestApi.getTest("test").await())} catch (e: Exception) {prink(e)}
}//4.搭配LiveData的进阶用法
interface HistoryApi {//用了post请求,并且加了suspend,直接让接口返回对象本身@POST(AppConfig.BO_API + "/play/list")suspend fun getPlayHistory(@Body body: RequestBody): WatchHistoryData
}class HistoryViewModel : BaseViewModel() {private var historyApi = NetWorkManager.historyApiSingleton//声明并初始化一个LiveData对象var playHistoryData: MutableLiveData<WatchHistoryData> = MutableLiveData()fun getPlayHistory(page: Int = 0, pageSize: Int = 12, type: String = XMediaConst.CONTENT_TYPE_VOD) = launchIO {var parameter: HashMap<String, Any> = hashMapOf()parameter["type"] = typeparameter["page"] = pageparameter["pageSize"] = pageSize//调用方法返回对象val resultData = historyApi.getPlayHistory(parameter.getBody())if (resultData.resultCode == 0)//让liveData对象绑定接口返回对象值playHistoryData.postValue(resultData)}
}//在starObserve中做回调监听,playHistoryData是一个liveData类型,当数据改变就会回调
historyViewModel.run {//this传的是一个lifecycleOwner对象,通常是activity或fragment持有playHistoryData.observe(this@VodFragment) {}
}

Network包中的组成:

api包:存放所有网络请求接口API

interface HistoryApi {//用了post请求,并且加了suspend,直接让接口返回对象本身@POST(AppConfig.BO_API + "/play/list")suspend fun getPlayHistory(@Body body: RequestBody): WatchHistoryData
}

bean包:存放Bean对象

data class WatchHistoryData(val contents: List<Content>,val description: String,val pageCount: Int,val resultCode: Int
)

viewModel包:存放ViewModel实现类

NetworkManager: 用于retrofit网络请求头参数统一配置,retrofit的对象初始化,以及初始化viewModel实现类对象

// lazy默认模式就是同步锁,其余模式暂不展开
val historyApiSingleton: HistoryApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {retrofit.create(HistoryApi::class.java)}val client = OkHttpClient.Builder()//.cache(Cache(file, cacheSize)).connectTimeout(DEFAULT_TIME, TimeUnit.SECONDS) //连接超时设置.readTimeout(DEFAULT_TIME, TimeUnit.SECONDS) //读取超时设置.writeTimeout(WRITE_TIME, TimeUnit.SECONDS) //写入超时设置.addInterceptor(headInterceptor()) //请求头拦截.addInterceptor(ChuckInterceptor(context)) //chunk工具类拦截//.addNetworkInterceptor(responseCacheInterceptor()) //缓存拦截.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) //提交和接收数据的拦截器.build()var retrofit = Retrofit.Builder().baseUrl(AppConfig.SERVER_ROOT_URI).addConverterFactory(GsonConverterFactory.create()) //gson解析.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //Rxjava.addCallAdapterFactory(CoroutineCallAdapterFactory()) //协程Coroutine.client(client).build()
3.功能模块和界面模块

功能模块没什么好讲的,就是功能封装,调用方式也很简洁,而界面模块,主要描述一下MVVM的Activity是怎么实现的

//HomeViewModel,即我们之前将的ViewModel实现类, TabletActivityCategoryBinding则是系统通过xml自动生成的,具体请看下述的xml
class TabletCategoryActivity : BaseVMActivity<HomeViewModel, TabletActivityCategoryBinding>() {//分类内容列表,具体数据需要调网络请求接口获取private var categoryContentList: MutableList<CategoryContentData.Content> = mutableListOf()private var categoryAdapter: TabletCategoryAdapter? = nullcompanion object {const val CATEGORY_ID = "category_id"const val CATEGORY_TYPE = "category_type"const val CATEGORY_TITLE = "category_title"}//HomeViewModel初始化,BaseVMActivity重写方法1override fun initVM(): HomeViewModel = HomeViewModel()//指定xml布局id,BaseVMActivity重写方法2override fun provideContentViewId(): Int = R.layout.tablet_activity_categoryoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)initRecyclerView()initData()}private fun initData() {//判断跳转前是否携带CATEGORY_ID参数intent.getStringExtra(CATEGORY_ID)?.let {if (it.isNotEmpty()) {//调用getCategoryContentList()的网络请求接口viewModel.getCategoryContentList(it)}}//tv_title自动绑定视图 是在app/build.gradle 中开启了buildFeatures { dataBinding = true }intent.getStringExtra(CATEGORY_TITLE)?.let {tv_title.text = it}}//recyclerView的初始化private fun initRecyclerView() {categoryAdapter = TabletCategoryAdapter()recycler_view.layoutManager = GridLayoutManager(this, 6)recycler_view.addItemDecoration(GridSpacingItemDecoration(6, 6f.dpInt, false))recycler_view.adapter = categoryAdaptercategoryAdapter?.setOnItemClickListener { _, _, position ->TabletVodDetailsActivity.actionActivity(this,categoryContentList[position].contentId,0)}nav_back.setOnClickListener { finish() }}//监听viewModel实现类中的LiveData数据,BaseVMActivity重写方法3override fun startObserve() {viewModel.categoryContentListData.observe(this, { categoryContentData ->//categoryContentData即网络请求结果对象                                            categoryContentData.contents.let {if (it.isNotEmpty()) {categoryContentList.addAll(it)//刷新数据categoryAdapter?.setList(it)}}})}
}
?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android=""xmlns:app=""xmlns:tools=""><-- 生成TabletActivityCategoryBinding类的关键 --><data></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/theme_bg"android:fitsSystemWindows="true"><ImageViewandroid:id="@+id/nav_back"android:layout_width="32dp"android:layout_height="32dp"android:layout_marginStart="14dp"android:clickable="true"android:focusable="true"android:padding="8dp"android:src="@drawable/nav_back"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/main_text_color"android:textSize="24sp"app:layout_constraintBottom_toBottomOf="@id/nav_back"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="@id/nav_back" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="34dp"app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@id/nav_back"app:spanCount="6"tools:listitem="@layout/tablet_item_category_vertical" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>

MVVM模式总结:

相关推荐

Android Jetpack 架构开发组件化应用实战

Android 组件化工程结构以及项目具体实施方案

一篇文章搞懂 Android 组件化

Android 彻底组件化—如何使用 Arouter

Android路由框架ARouter的集成、基本使用以及踩坑全过程

我是code小生,喜欢可以随手点个在看、转发给你的朋友,谢谢~


如果你有写博客的好习惯
欢迎投稿
点个“在看”小生感恩❤️

更多推荐

MVVM+组件化+Arouter实现

本文发布于:2024-02-28 03:22:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1768191.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:组件   MVVM   Arouter

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!