SkillAgentSearch skills...

MVVMKit

使用Kotlin搭建Android MVVM快速开发框架。

Install / Use

/learn @shenbengit/MVVMKit

README

MVVMKit

使用Kotlin搭建Android MVVM快速开发框架。

引入

注意:2.x.x版本与1.x.x版本不兼容

将JitPack存储库添加到您的项目中(项目根目录下build.gradle文件)

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

添加依赖(项目目录下build.gradle文件)

从v1.1.0版本开始,minSdkVersion调整为21,分支1.0.x版本minSdkVersion19

plugins {
    ...
    id 'kotlin-kapt'
}

android{
    ...
    buildFeatures {
        //启用databinding
        dataBinding = true
    }
}

dependencies {
    implementation 'com.github.shenbengit:MVVMKit:Tag'
}

演示用例

详见SrsRtcAndroidClient

如果您想快速使用,您需要先了解Kotlin语法、Databinding、Koin等相关知识。

项目中集成的三方组件

Android Jetpack

  • Lifecycle:生命周期感知型组件可执行操作来响应另一个组件(如 Activity 和 Fragment)的生命周期状态的变化;
  • LiveData:LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期;
  • ViewModel:ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据;

网络请求

  • okhttp:OkHttp is an HTTP client that’s efficient;
  • Retrofit:A type-safe HTTP client for Android and Java;

依赖注入

  • koin:一个实用的轻量级Kotlin依赖注入框架;

其他

  • Moshi:Moshi是一个适用于Android、Java和Kotlin的JSON 库;
  • Glide:一个适用于Android的图像加载和缓存库,专注于平滑滚动;
  • coroutines:Kotlin coroutines;
  • MMKV:MMKV是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强,代替SharedPreferences;
  • SFragmentation:框架负责管理fragment的各种操作,相比于google新出的navigation框架,更加灵活多变,易于使用;
  • Toasty:吐司;
  • LoadingDialog:Android LoadingDialog;
  • BRV:Android 快速构建 RecyclerView, 比 BRVAH 更简单强大;

快速使用

初始化

建议在Application中执行.

class App : Application() {

    override fun onCreate() {
        super.onCreate()

        InitEnvironment.init(this, object : InitEnvironment.ConfigurationEnvironment {

            override val debug: Boolean
                get() = BuildConfig.DEBUG

            override val mmkvMode: Int
                get() = super.mmkvMode

            override val mmkvCryptKey: String?
                get() = super.mmkvCryptKey

            override fun logV(tag: String, msg: () -> Any) {
                Log.v(tag, msg().toString())
            }

            override fun logD(tag: String, msg: () -> Any) {
                if (debug) {
                    Log.d(tag, msg().toString())
                }
            }

            override fun logI(tag: String, msg: () -> Any) {
                Log.i(tag, msg().toString())
            }

            override fun logW(tag: String, msg: () -> Any) {
                Log.w(tag, msg().toString())
            }

            override fun logE(tag: String, msg: () -> Any) {
                Log.e(tag, msg().toString())
            }
        }) {
            val koinApplication =
                KoinAndroidApplication
                    .create(
                        this,
                        if (BuildConfig.DEBUG) Level.ERROR else Level.ERROR
                    )
                    .modules(appModule)
            globalInit(koinApplication)

//            initToasty()
//            initMMKV()
//            initFragmentation()
//            initKoin(koinApplication)
        }
    }
}

基类

BaseSupportActivity

一般来说我们自己的项目都会有自己的BaseActivity用于封装基础逻辑,所以需要我们的基类要继承BaseSupportActivity,自定义相关方法。

abstract class BaseActivity<VM : BaseViewModel<out IRepository>, VDB : ViewDataBinding> :
    BaseSupportActivity<VM, VDB>(), CustomAdapt {

    override fun isBaseOnWidth(): Boolean {
        return true
    }

    override fun getSizeInDp(): Float {
        return Constant.DEFAULT_SIZE_IN_DP
    }

    override fun initLoadingDialog(): Dialog {
        return LoadingDialog.builder(this)
            .setHintText(getString(R.string.loading))
            .create()
    }
}

使用示例

class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>() {
    /**
     * 如果不重写,将反射获取
     */
//    override fun getLayoutId(): Int {
//        return R.layout.activity_main
//    }

    override fun injectViewModel(): Lazy<MainViewModel> {
        return viewModel()
    }

    override fun getViewModelId(): Int {
        return BR.viewModel
    }

    override fun initView() {

    }

    override fun initData(savedInstanceState: Bundle?) {

    }
}

BaseSupportFragment

同BaseSupportActivity.

abstract class BaseFragment<VM : BaseViewModel<out IRepository>, VDB : ViewDataBinding> :
    BaseSupportFragment<VM, VDB>(), CustomAdapt {
    
    override fun isBaseOnWidth(): Boolean {
        return true
    }

    override fun getSizeInDp(): Float {
        return Constant.DEFAULT_SIZE_IN_DP
    }

    override fun initLoadingDialog(): Dialog {
        return LoadingDialog.builder(requireContext())
            .setHintText(getString(R.string.loading))
            .create()
    }
}

使用示例

class TestFragment : BaseFragment<DefaultViewModel, FragmentTestBinding>() {

    /**
     * 如果不重写,将反射获取
     */
//    override fun getLayoutId(): Int {
//        return R.layout.fragment_test
//    }

    /**
     * 注入ViewModel
     * @see [viewModel]
     * @see [sharedViewModel]
     * @see [sharedStateViewModel]
     */
    override fun injectViewModel(): Lazy<DefaultViewModel> {
        return viewModel()
    }

    override fun getViewModelId(): Int {
        return BR.viewModel
    }

    override fun initView() {
    }

    override fun initData(savedInstanceState: Bundle?) {
    }
}

BaseViewModel

ViewMode的基类,感知View生命周期;

class MainViewModel(
    application: Application,
    repo: BaseNothingRepository
) : BaseViewModel<BaseNothingRepository>(application, repo) {

    override fun onCreate(owner: LifecycleOwner) {
        ...
    }
    
    override fun onDestroy(owner: LifecycleOwner) {
        ...
    }
}

如果你的ViewModel中不需要写相关逻辑,比较简单,则可以用DefaultViewModel进行占位。

BaseRepository

数据仓库基类,在创建BaseViewModel需要使用,自行创建并继承相关类即可;

/**
 * 网络请求和本地数据库均需要使用的情况
 */
open class BaseBothRepository<T : IRemoteDataSource, R : ILocalDataSource>(
    protected val remoteDataSource: T,
    protected val localDataSource: R
) : BaseRepository()

/**
 * 仅使用本地数据库
 */
open class BaseLocalRepository<T : ILocalDataSource>(
    protected val remoteDataSource: T
) : BaseRepository()

/**
 * 仅使用网络请求
 */
open class BaseRemoteRepository<T : IRemoteDataSource>(
    protected val remoteDataSource: T
) : BaseRepository()

/**
 * 不需要数据
 */
class BaseNothingRepository : BaseRepository()

如果你没有数据请求相关操作,则可以用BaseNothingRepository进行占位。

网络请求(Retrofit + Coroutines)

BaseRetrofitClient

自定义RetrofitClient,示例:

class RetrofitClient : BaseRetrofitClient() {

    companion object {
        private const val TAG = "RetrofitClient"
        private const val DEFAULT_MILLISECONDS: Long = 30
    }

    private lateinit var apiService: ApiService

    override fun generateOkHttpBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
        val interceptor = HttpLoggerInterceptor { message -> logI(TAG) { message } }
        interceptor.level = HttpLoggerInterceptor.Level.BODY

        return builder.readTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
            .writeTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
            .connectTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
            .addInterceptor(interceptor)
    }

    override fun generateRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
        return builder.apply {
            val moshi = Moshi.Builder()
                .addLast(NullSafeKotlinJsonAdapterFactory())
                .addLast(NullSafeStandardJsonAdapters.FACTORY)
                .build()
            addConverterFactory(MoshiConverterFactory.create(moshi))
        }
    }

    /**
     * 动态修改Retrofit-baseUrl
     */
    fun setBaseUrl(baseUrl: String) {
        apiService = getApiService(ApiService::class.java, baseUrl, false)
    }

    fun getApiService(): ApiService {
        if (this::apiService.isInitialized.not()) {
            setBaseUrl("https://api.github.com/")
        }
        return apiService
    }

}

文件下载(DownloadRetrofitClient)

支持md5文件校验,暂不支持断点续传;
使用示例:

lifecycleScope.launch {
    // 1
    downloadFile(url = "", filePath = "", md5 = "") {
        start {

        }
        progress { totalSize, downloadSize, progress ->

        }
        success { file, fileMd5, md5VerifySuccess ->

        }
        error {

        }
    }.startDownload()

    // 2
    DownloadFile(url = "", filePath = "", md5 = "").start {

    }.progress { totalSize, downloadSize, progress ->

    }.success { file, fileMd5, md5VerifySuccess ->

    }.error {

    }.startDownload()
}

BaseResponse(网络请求接口返回继承基类)

这个是在BaseViewModel中快速使用网络请求所用,不强制使用。
一般来说网络请求结果返回都会有对应的状态返回,如成功、失败、错误等。
这样在返回结果实体bean中去继承此类,则可以快速执行条件判断,如下:

open class ApiResponse<T>(
    @Json(name = "code")
    val code: Int,
    @Json(name = "data")
    val data: T?,
    @Json(name = "msg")
    val msg: String
) : BaseResponse<T> {

    override fun isSuccess(): Boolean {
        return code == 200
    }

    override fun getResponseCode(): Int {
        return code
    }

    override fun getResponseMsg(): String {
        return msg
    }

    override fun
View on GitHub
GitHub Stars37
CategoryDevelopment
Updated22d ago
Forks8

Languages

Kotlin

Security Score

95/100

Audited on Mar 10, 2026

No findings