MVVMKit
使用Kotlin搭建Android MVVM快速开发框架。
Install / Use
/learn @shenbengit/MVVMKitREADME
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版本minSdkVersion为19;
plugins {
...
id 'kotlin-kapt'
}
android{
...
buildFeatures {
//启用databinding
dataBinding = true
}
}
dependencies {
implementation 'com.github.shenbengit:MVVMKit:Tag'
}
演示用例
如果您想快速使用,您需要先了解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
