懒人必备Android谷歌Jetpack MVVM框架

目录

Document

1.如何集成

2.继承基类

3.编写一个网络请求功能

4.网络请求(Retrofit+协程)

5.下载方法介绍

6.写了一些常用的拓展函数

7.项目使用的三方库及其简单示例和资料

版本更新历史:

QQ 群:458173716

github地址


一个灵活、高效的Android Jetpack MVVM开发框架,基于AndroidX开发,傻瓜式使用,适用于所有项目,强烈建议以依赖的方式导入,这样避免代码同步,有问题或建议?请通过博客、qq群联系我们。

谷歌 Android 团队 Jetpack 视图模型: 

演示

框架UI效果图如下:

 

框架版本更新说明:
新版3.x.x已发布 新增Jetpack hilt组件,不熟悉hilt组件的建议使用2.x.x版本

Document

1.如何集成

  • 1.1 在root's build.gradle中加入Jitpack仓库
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • 1.2 在app's build.gradle中添加依赖
dependencies {
  ...
    //框架地址
    implementation 'com.github.cl-6666:mvvm-framework:v3.1.0'
    //hilt官方地址:https://developer.android.google.cn/training/dependency-injection/hilt-android?hl=zh-cn
    def hilt_version = "2.48.1"
    api "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}
  • 1.3 在app's build.gradle中,android 模块下按需开启DataBinding与ViewBinding
AndroidStudio 4.0 以下版本------>
android {
    ...
    dataBinding {
        enabled = true 
    }
    viewBinding {
        enabled = true
    }
}

AndroidStudio 4.0及以上版本 ------>
android {
    ...
   buildFeatures {
        dataBinding = true
        viewBinding = true
    }
}
  • 1.4所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注解的 Application 类
@HiltAndroidApp
class AppApplication : BaseApplication() { ... }
如果由于某种原因,导致你不能继承[BaseApplication];你也可以在你自定义的Application的onCreate函
数中通过调用[BaseApplication.initAppConfig]来进行初始化

Hilt 会按照相应 Android 类的生命周期自动创建和销毁生成的组件类的实例。

生成的组件创建时机销毁时机
SingletonComponentApplication#onCreate()Application 已销毁
ActivityRetainedComponentActivity#onCreate()Activity#onDestroy()
ViewModelComponentViewModel 已创建ViewModel 已销毁
ActivityComponentActivity#onCreate()Activity#onDestroy()
FragmentComponentFragment#onAttach()Fragment#onDestroy()
ViewComponentView#super()View 已销毁
ViewWithFragmentComponentView#super()View 已销毁
ServiceComponentService#onCreate()Service#onDestroy()

2.继承基类

一般我们项目中都会有一套自己定义的符合业务需求的基类 BaseActivity/BaseFragment,所以我们的基类需要继承本框架的Base类

  • 不想用Databinding与ViewBinding-------可以继承 BaseVmActivity/BaseVmFragment
  • 用Databinding-----------可以继承BaseVmDbActivity/BaseVmDbFragment**
  • 用Viewbinding-----------可以继承BaseVmVbActivity/BaseVmVbFragment**

Activity:

abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbActivity<VM, DB>() {
     /**
     * 当前Activity绑定的视图布局Id abstract修饰供子类实现
     */
    abstract override fun layoutId(): Int
    /**
     * 当前Activityc创建后调用的方法 abstract修饰供子类实现
     */
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 创建liveData数据观察
     */
    override override fun createObserver()

    /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

Fragment:

abstract class BaseFragment<VM : BaseViewModel,DB:ViewDataBinding> : BaseVmDbFragment<VM,DB>() {
   
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 懒加载 只有当前fragment视图显示时才会触发该方法 abstract修饰供子类实现
     */
    abstract override fun lazyLoadData()

    /**
     * 创建liveData数据观察 懒加载之后才会触发
     */
    override override fun createObserver()
  
    /**
     * Fragment执行onViewCreated后触发的方法 
     */
    override fun initData() {

    }
    
   /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

3.编写一个网络请求功能

  • 3.1 创建MainViewModel类继承BaseViewModel
@HiltViewModel
class MainViewModel @Inject constructor(
    private val apiService: ApiService
) : BaseViewModel() {}
  • 3.2 创建HomeFragment 继承基类传入相关泛型,第一个泛型为你创建的MainViewModel,第二个泛型为ViewDataBind,保存fragment_home.xml后databinding会生成一个FragmentHomeBinding类。(如果没有生成,试着点击Build->Clean Project)
@AndroidEntryPoint
class HomeFragment : BaseFragment<MainViewModel, FragmentHomeBinding>() {
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
}

4.网络请求(Retrofit+协程)

  • 4.1 新建请求配置类继承 BaseNetworkApi 示例:
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule : BaseNetworkApi(){

    /**
     * 实现重写父类的setHttpClientBuilder方法,
     * 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作
     */
    override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
       //普通网络日志显示写法
        val httpLoggingInterceptor = HttpLoggingInterceptor { message ->
            Timber.i(
                "网络日志", message
            )
        }
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        //框架内部封装日志显示写法 hideVerticalLine = true代表隐藏横线
        val loggingInterceptor = AndroidLoggingInterceptor.build(hideVerticalLine = true)

        builder.apply {
            /** 设置缓存配置 缓存最大10M */
            cache(Cache(File(app.cacheDir, "cxk_cache"), 10 * 1024 * 1024))
            /** 添加Cookies自动持久化 */
//            cookieJar(cookieJar)
            /** 演示添加缓存拦截器 可传入缓存天数,不传默认7天 */
            addInterceptor(CacheInterceptor())
            /** 演示添加公共heads 注意要设置在日志拦截器之前,不然Log中会不显示head信息 */
            addInterceptor(MyHeadInterceptor())
            /** 演示token过期拦截器演示 */
            addInterceptor(TokenOutInterceptor())
            /** 演示日志拦截器 */
            addInterceptor(loggingInterceptor)
            /** 超时时间 连接、读、写 */
            connectTimeout(10, TimeUnit.SECONDS)
            readTimeout(5, TimeUnit.SECONDS)
            writeTimeout(5, TimeUnit.SECONDS)
        }
        return builder
    }

    /**
     * 实现重写父类的setRetrofitBuilder方法,
     * 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,protobuf等
     */
    override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
        return builder.apply {
            addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
        }
    }

    @Provides
    fun provideApiService(): ApiService {
        return NetworkModule.getApi(ApiService::class.java, Constants.BASE_URL, false)
    }
}
  • 4.2如果你请求服务器返回的数据有基类(没有可忽略这一步)例如:
{
    "data": ...,
    "errorCode": 0,
    "errorMsg": ""
}

该示例格式是 玩Android Api返回的数据格式,如果errorCode等于0 请求成功,否则请求失败 作为开发者的角度来说,我们主要是想得到脱壳数据-data,且不想每次都判断errorCode==0请求是否成功或失败 这时我们可以在服务器返回数据基类中继承BaseResponse,实现相关方法:

data class ApiResponse<T>(var errorCode: Int, var errorMsg: String, var data: T) : BaseResponse<T>() {

    // 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来编写
    override fun isSucces() = errorCode == 0

    override fun getResponseCode() = errorCode

    override fun getResponseData() = data

    override fun getResponseMsg() = errorMsg

}
  • 4.3 在ViewModel中发起请求,所有请求都是在viewModelScope中启动,请求会发生在IO线程,最终回调在主线程上,当页面销毁的时候,请求会统一取消,不用担心内存泄露的风险,框架做了2种请求使用方式

1、将请求数据包装给ResultState,在Activity/Fragment中去监听ResultState拿到数据做处理

@HiltViewModel
class MainViewModel @Inject constructor(
    private val apiService: ApiService
) : BaseViewModel() {


    @Inject
    lateinit var downLoadManager: DownLoadManager

    /**
     *自动脱壳过滤处理请求结果,自动判断结果是否成功
     */
    var articleListResult = MutableLiveData<ResultState<Data>>()

    /**
     * 不用框架帮脱壳
     */
    var articleListResult2 = MutableLiveData<ResultState<ApiResponse<Data>>>()

    /**
     * 网络请求
     */
    fun apiArticleListData() {
        //1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
        request(
            { apiService.getEntryAndExitData() }, //请求体
            articleListResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
            true,//是否显示等待框,,默认false不显示 可以默认不传
            "数据请求中..."//等待框内容,可以默认不填请求网络中...
        )
        //2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
        requestNoCheck(
            { apiService.getEntryAndExitData() },
            articleListResult2,
            true,
            "数据请求中..."
        )
    }
}

@AndroidEntryPoint
class HomeFragment : BaseFragment<MainViewModel,FragmentHomeBinding>(){

    private val mArticleListAdapter: ArticleListAdapter by lazy { ArticleListAdapter(arrayListOf()) }

    override fun initView(savedInstanceState: Bundle?) {
        mViewModel.apiArticleListData()
        mDatabind.rvArticleList.init(LinearLayoutManager(activity), mArticleListAdapter, false)
    }

    override fun createObserver() {
        super.createObserver()
        //脱壳
        mViewModel.articleListResult.observe(viewLifecycleOwner,
            Observer { resultState ->
                parseState(resultState, {
                    //请求成功 打印消息
                    mArticleListAdapter.submitList(it.datas)
                }, {
                    //请求失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
                    toast("请求失败")
                })
            })


        //不脱壳
        mViewModel.articleListResult2.observe(viewLifecycleOwner) { resultState ->
            parseState(resultState, {
                if (it.errorCode == 0) {
                    //请求成功 打印消息
                    toast(it.data.toString())
                } else {
                    //请求失败
                    toast(it.errorMsg)
                }
            }, {
                //请求发生了异常
                toast(it.errorMsg)
            })
        }
    }
}

2、 直接在当前ViewModel中拿到请求结果 

@HiltViewModel
class MyFragmentViewModel @Inject constructor(
    private val apiService: ApiService
): BaseViewModel() {
    
    fun testData() {
        //1.拿到已脱壳的数据(项目有基类的可以用)
        request({ apiService.getEntryAndExitData() }, {
            //请求成功 已自动处理了 请求结果是否正常
        }, {
            //请求失败 网络异常,或者请求结果码错误都会回调在这里
        }, true, "正在登录中...")

        //2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类或者不想框架帮忙脱壳的可以用)
        requestNoCheck({ apiService.getEntryAndExitData() }, {
            //请求成功 自己拿到数据做业务需求操作
            if (it.errorCode == 0) {
                //结果正确
            } else {
                //结果错误
            }
        }, {
            //请求失败 网络异常回调在这里
        }, true, "正在登录中...")
    }
}
}
 

3、请求结果截图 

演示

5.下载方法介绍

    //注入方法
   @Inject
    lateinit var downLoadManager: DownLoadManager

   /**
     * 下载演示
     */
    suspend fun downloadPictures() {
//        val apkDir = File(Environment.getExternalStorageDirectory(), "YourApp/apks")
//        apkDir.mkdirs()
//        val apkFile = File(apkDir, "your_apk_file_name.apk")
        downLoadManager.downLoad("TAG",
            "http://172.31.51.252:9999/hybd/bagl/app/1.2.1_20240220.apk",
            "",
            "",
            reDownload = false,
            whetherHttps = false,
            object : OnDownLoadListener {
                override fun onDownLoadPrepare(key: String) {
                    TODO("Not yet implemented")
                }

                override fun onDownLoadError(key: String, throwable: Throwable) {
                    TODO("Not yet implemented")
                }

                override fun onDownLoadSuccess(key: String, path: String, size: Long) {
                    TODO("Not yet implemented")
                }

                override fun onDownLoadPause(key: String) {
                    TODO("Not yet implemented")
                }

                override fun onUpdate(
                    key: String,
                    progress: Int,
                    read: Long,
                    count: Long,
                    done: Boolean
                ) {
                    TODO("Not yet implemented")
                }
            })
    }

6.写了一些常用的拓展函数

算了不写了,这个不重要,想具体看的话可以在
 me.hgj.jetpackmvvm.ext.util
 me.hgj.jetpackmvvm.ext.view
 的包中看,反正你也可以自己写,按照自己的喜好与需求来

7.项目使用的三方库及其简单示例和资料

  • Kotlin
  • Jetpack
    • Lifecycle: 观察 Android 生命周期并根据生命周期变化处理 UI 状态
    • ViewModel: 管理与 UI 相关的数据持有者和生命周期感知。 允许数据在配置更改(例如屏幕旋转)中保存下来。
    • ViewBinding: 使用声明性格式而不是以编程方式将布局中的 UI 组件绑定到应用程序中的数据源。
    • Room: 通过在 SQLite 上提供抽象层来构建数据库,以允许流畅的数据库访问。
    • Hilt: 用于依赖注入。
  • LiveData
  • ksp: Kotlin 符号处理 API。
  • coilCoil地址: Coil 是一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片。
  • Timber: 日志框架
  • OkHttp:网络请求
  • PersistentCookieJar:持久CookieJar实现
  • logging-interceptor:网络日志
  • Retrofit:网络请求

版本更新历史:

  • v3.1.0:(2024年04月06日) hilt版本

    • 增加coil图片框架
    • 第三方库升级
    • hilt相关组件封装
    • 代码优化
  • v2.1.2:(2024年04月06日) 普通版本

    • 增加coil图片框架
    • 第三方库升级
    • 代码优化

QQ 群:458173716

github地址

github地址:GitHub - cl-6666/mvvm-framework: 基于谷歌最新AAC架构,MVVM设计模式的一套快速开发库

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值