一个Jetpack结合MVVM的快速开发框架

2 篇文章 0 订阅
1 篇文章 0 订阅

🐔🐔🐔JetPackMvvm

  • 基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation组件
  • 使用kotlin语言,添加大量拓展函数,简化代码
  • 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速请求网络

演示Demo

已用该库重构了我之前的玩安卓项目,利用Navigation组件以单Activity+Fragment架构编写,优化了很多代码,对比之前的mvp项目,开发效率与舒适度要提高了不少,想看之前MVP的项目可以去 https://github.com/hegaojian/WanAndroid

效果图展示

项目效果图

APK下载:

1.如何集成

  • 1.1 在root’s build.gradle中加入Jitpack仓库
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • 1.2 在app’s build.gradle中添加依赖
dependencies {
  ...
  implementation 'me.hegj:JetpackMvvm:1.1.0'
}
  • 1.3 在app’s build.gradle中,android 模块下开启DataBinding(如果你不想用DataBinding,请忽略这一步)
android {
    ...
    dataBinding {
        enabled = true 
    }
}

2.继承基类

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

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>() {
    /**
     * 当前Fragment绑定的视图布局Id abstract修饰供子类实现
     */
    abstract override fun layoutId(): Int
   
    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 编写fragment_login.xml界面后转换成 databind 布局(鼠标停在根布局,Alt+Enter 点击提示 Convert to data binding layout即可)
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/tools">
    <data>
       
    </data>
    <LinearLayout>
       ....
    </LinearLayout>
 </layout>   
  • 3.2 创建LoginViewModel类继承BaseViewModel
class LoginViewModel(application: Application) : BaseViewModel(application) {
  
}
  • 3.3 创建LoginFragment 继承基类传入相关泛型,第一个泛型为你创建的LoginViewModel,第二个泛型为ViewDataBind,保存fragment_login.xml后databinding会生成一个FragmentLoginBinding类。(如果没有生成,试着点击Build->Clean Project)
class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    /**
     *  当前fragment绑定的布局
     */
    override fun layoutId() = R.layout.fragment_login
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
}

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

  • 4.1 新建请求配置类继承 BaseNetworkApi 示例:
class NetworkApi : BaseNetworkApi() {

   companion object {
         
        val instance: NetworkApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { NetworkApi() }

        //双重校验锁式-单例 封装NetApiService 方便直接快速调用
        val service: ApiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            instance.getApi(ApiService::class.java, ApiService.SERVER_URL)
        }
    }
   
    /**
     * 实现重写父类的setHttpClientBuilder方法,
     * 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意你想要做的骚操作
     */
    override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
        builder.apply {
            //示例:添加公共heads,可以存放token,公共参数等, 注意要设置在日志拦截器之前,不然Log中会不显示head信息
            addInterceptor(MyHeadInterceptor())
            // 日志拦截器
            addInterceptor(LogInterceptor())
            //超时时间 连接、读、写
            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()))
            addCallAdapterFactory(CoroutineCallAdapterFactory())
        }
    }
}
  • 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拿到数据做处理

class RequestLoginViewModel(application: Application) : BaseViewModel(application) {

  //自动脱壳过滤处理请求结果,自动判断结果是否成功
    var loginResult = MutableLiveData<ResultState<UserInfo>>()
    
  //不用框架帮脱壳
    var loginResult2 = MutableLiveData<ResultState<ApiResponse<UserInfo>>>()
    
  fun login(username: String, password: String){
   //1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
        request(
            { HttpRequestManger.instance.login(username, password) }, //请求体
            loginResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
            true,//是否显示等待框,,默认false不显示 可以默认不传
            "正在登录中..."//等待框内容,可以默认不填请求网络中...
        )
        
   //2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
        requestNoCheck(
          {HttpRequestManger.instance.login(username,password)},
          loginResult2,
          true,
          "正在登录中...") 
}


class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    /** 注意,在by lazy中使用getViewModel一定要使用泛型,虽然他提示不报错,但是你不写是不行的 */
    private val requestLoginRegisterViewModel: RequestLoginRegisterViewModel by lazy { getViewModel<RequestLoginRegisterViewModel>() }
    
    /**
     *  当前fragment绑定的布局
     */
    override fun layoutId() = R.layout.fragment_login
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
    
    override fun createObserver(){
      //脱壳
       requestLoginRegisterViewModel.loginResult.observe(viewLifecycleOwner,
            Observer { resultState ->
                parseState(resultState, {
                    //登录成功 打印用户
                    it.username.logd()
                }, {
                    //登录失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
                    showMessage(it.errorMsg)
                })
            })
    
       //不脱壳
       requestLoginRegisterViewModel.loginResult2.observe(viewLifecycleOwner, Observer {resultState ->
               parseState(resultState,{
                   if(it.errorCode==0){
                       //登录成功 打印用户名
                       it.data.username.logd()
                   }else{
                       //登录失败
                       showMessage(it.errorMsg)
                   }
               },{
                   //请求发生了异常
                   showMessage(it.errorMsg)
               })
           })
   } 
}

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

class RequestLoginViewModel(application: Application) : BaseViewModel(application) {
    
  fun login(username: String, password: String){
   //1.拿到已脱壳的数据(项目有基类的可以用)
     request({HttpRequestManger.instance.login(username,password)},{
             //请求成功 已自动处理了 请求结果是否正常
             it.username.logd()
         },{
             //请求失败 网络异常,或者请求结果码错误都会回调在这里
             it.errorMsg.logd()
         },true,"正在登录中...")
        
   //2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
       requestNoCheck({HttpRequestManger.instance.login(username,password)},{
            //请求成功 自己拿到数据做业务需求操作
            if(it.errorCode==0){
                //结果正确
                it.data.username.logd()
            }else{
                //结果错误
                it.errorMsg.logd()
            }
        },{
            //请求失败 网络异常回调在这里
            it.errorMsg.logd()
        },true,"正在登录中...")
}

5.获取ViewModel

  • 5.1我们的activity/fragment会有多个ViewModel,按官方的写感觉有点累
 val mainViewModel = ViewModelProvider(this,
            ViewModelProvider.AndroidViewModelFactory(application)).get(MainViewModel::class.java)

**优化了一下改成了

//在activity中获取当前Activity级别作用域的ViewModel
 val mainViewModel = getViewModel<MainViewModel>()
 
//在activity中获取Application级别作用域的ViewModel(注,Application类继承框架的BaseApp类才有用)
 val mainViewModel = getAppViewModel<MainViewModel>()

//在fragment中获取当前Fragment级别作用域的ViewModel
val mainViewModel = getViewModel<MainViewModel>()

//在fragment中获取父类Activity级别作用域的ViewModel
val mainViewModel = getActivityViewModel<MainViewModel>()

//在fragment中获取Application级别作用域的ViewModel(注,Application类继承框架的BaseApp类才有用)
val mainViewModel = getAppViewModel<MainViewModel>()

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

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

License

 Copyright 2019, hegaojian(何高建)       
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at 
 
       http://www.apache.org/licenses/LICENSE-2.0 

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jetpack一个结合MVVM快速开发框架,它基于MVVM模式并集成了谷歌官方推荐的Jetpack组件库,包括LiveData、ViewModel、Lifecycle和Navigation组件。这个框架使用Kotlin语言,并添加了大量的拓展函数,以简化代码。它还集成了Retrofit网络请求和协程,可以帮助开发者快速开发项目。\[1\] MVVM是一种软件架构模式,它将应用程序分为三个主要部分:模型(Model)、视图(View)和视图模型(ViewModel)。在MVVM中,视图负责显示数据和用户交互,模型负责处理数据和业务逻辑,而视图模型则充当视图和模型之间的中间层,负责管理视图的状态和数据。JetpackMVVM模式可以帮助开发者更好地组织和管理代码,提高开发效率和舒适度。\[1\] 使用JetpackMVVM可以带来许多好处,例如简化代码、提高开发效率、提供更好的代码结构和可维护性。通过使用Jetpack的组件库,开发者可以更轻松地处理生命周期管理、数据共享和导航等常见任务。而MVVM模式则可以帮助开发者更好地分离关注点,使代码更易于测试和维护。\[2\] 总之,JetpackMVVM是一种强大的组合,可以帮助开发者快速开发Android应用程序,并提供更好的代码结构和可维护性。如果你想了解更多关于JetpackMVVM的信息,可以参考引用\[1\]中提供的Jetpack框架的介绍。 #### 引用[.reference_title] - *1* *2* [JetpackMvvm](https://blog.csdn.net/u014608640/article/details/124711159)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [大型Android项目架构:基于组件化+模块化+Kotlin+协程+Flow+Retrofit+Jetpack+MVVM架构实现WanAndroid...](https://blog.csdn.net/m0_37796683/article/details/130277908)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值