忿忿的泥巴坨 发表于 2025-3-26 05:02:05

Android项目实战搭建 MVVM架构

View层

详细代码:

 activity:

/**
* @description:
* 普通Activity基类,不带ViewModel,显示基本加载状态
* 需要获取到子类的布局id用于databinding的绑定
* @author YL Chen
* @date 2024/9/4 21:34
* @version 1.0
*/
abstract class BaseActivity<VB : ViewDataBinding>(@LayoutRes layoutID: Int) :
    AppCompatActivity() { //此处不能将layoutId传递进去,否则会导致fragment加载但不显示

    open lateinit var mRefreshLayout: SmartRefreshLayout
    //仅供直接继承本类的子类调用,继承于BaseVMActivity的调用此对象的方法无效,
    // 因为setSuccessView方法先于getLoadView方法执行,导致加载状态无法被移除,继承于BaseVMActivity的想改变状态需使用mViewModel调用changeStateView方法
    open lateinit var mMultiplyStateView: MultiplyStateView
    //子view的布局id
    private var mLayoutId: Int = layoutID

    //子View的dataBinding
    lateinit var mBinding: VB

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      //设置基础布局
      setContentView(R.layout.base_load_more_layout)
      //初始化界面
      initView()
      //初始化数据
      initData()
    }

    //初始化界面
    open fun initView() {
      //获取加载成功DataViewBinding
      mBinding = DataBindingUtil.inflate(
            layoutInflater,
            mLayoutId,
            null,
            false
      )
      //刷新框架
      mRefreshLayout = findViewById(R.id.refreshLayout)
      //找到基础布局中的自定义多状态View控件
      mMultiplyStateView = findViewById(R.id.multiply_state_view)
      // 将加载成功View布局添加到自定义多状态View中
      mMultiplyStateView.setSuccessView(mBinding.root)
    }

    //初始化数据
    abstract fun initData()
}



/**
* @description: 携带ViewModel的Activity基类,继承自BaseActivity
* @author YL Chen
* @date 2024/9/6 14:16
* @version 1.0
*/
abstract class BaseVMActivity<VB : ViewDataBinding, VM : BaseViewModel>(@LayoutRes layoutId: Int) :
    BaseActivity<VB>(layoutId) {
    //子类ViewModel实例
    lateinit var mViewModel: VM

    /**
   * 获取对应的ViewModel,并初始化数据
   */
    override fun initData() {
      //dataLoading()
      mViewModel = getViewModel()!!
      //将子类的ViewModel和dataBinding联系起来,实现界面数据的自动更新
      //将xml布局对应的viewModel对象赋值到xml布局中声明的viewModel变量 即实现如:mBinding.viewModel = ViewModel() 的效果
      val variableId = getVariableId()
      if (variableId != -1) {
            mBinding.setVariable(variableId, mViewModel)
            //立即执行 Data Binding 中的挂起绑定
            //即Data Binding 会立即将 ViewModel 的属性和方法更新到布局文件中
            mBinding.executePendingBindings()
      }
      //初始化视图状态
      initViewState()
      //初始化ViewModel数据
      initVMData()
      //监听liveData
      observeLiveData()
      //设置状态页点击重新加载监听
      mMultiplyStateView.setOnReLodListener(mViewModel)
    }

    /**
   * 监听ViewModel中的LiveData
   */
    open fun observeLiveData() {
    }

    /**
   * 初始化状态
   */
    private fun initViewState() {
      mViewModel.mStateViewLiveData.observe(this) {
            when (it) {
                ViewStateEnum.VIEW_LOADING -> {
                  LogUtils.d(this, "StateLayoutEnum.DATA_LOADING")
                  dataLoading()
                }

                ViewStateEnum.VIEW_EMPTY -> {
                  LogUtils.d(this, "StateLayoutEnum.DATA_ERROR")
                  dataEmpty()
                }

                ViewStateEnum.VIEW_NET_ERROR -> {
                  LogUtils.d(this, "StateLayoutEnum.NET_ERROR")
                  netError()
                }

                ViewStateEnum.VIEW_LOAD_SUCCESS -> {
                  LogUtils.d(this, "StateLayoutEnum.LOAD_SUCCESS")
                  loadSuccess()
                }

                ViewStateEnum.VIEW_NONE -> {
                  LogUtils.d(this, "StateLayoutEnum.NONE")
                }
            }
      }
    }

    /**
   * 数据加载成功
   */
    open fun loadSuccess() {
      mMultiplyStateView.showSuccess()
    }

    /**
   * 网络加载失败
   */
    open fun netError() {
      mMultiplyStateView.showNetError()
    }


    /**
   * 数据加载为空
   */
    open fun dataEmpty() {
      mMultiplyStateView.showEmpty()
    }

    /**
   * 数据加载中
   */
    open fun dataLoading() {
      mMultiplyStateView.showLoading()
      val loadingView = mMultiplyStateView.getLoadingView()
      val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
      myLoadingView.startRotate()
    }

    /**
   * 初始化ViewModel数据
   */
    abstract fun initVMData()

    /**
   * 获取xml绑定的variable
   * @return Int
   */
    //子类通过重写此方法返回子类对应xml文件中绑定的viewModel变量的id
    open fun getVariableId(): Int {
      return -1
    }

    /**
   * 通过反射获取子类的ViewModel
   * @return VM?
   */
    private fun getViewModel(): VM? {
      //这里获得到的是类的泛型的类型
      val type = javaClass.genericSuperclass
      if (type != null && type is ParameterizedType) {
            val actualTypeArguments = type.actualTypeArguments
            val tClass = actualTypeArguments
            return ViewModelProvider(
                this,
                ViewModelProvider.AndroidViewModelFactory.getInstance(application)
            )
                .get(tClass as Class<VM>)
      }
      return null
    }
} fragment:

/**
* @description: 普通Fragment基类,不带ViewModel
* @author YL Chen
* @date 2024/9/6 16:19
* @version 1.0
*/
abstract class BaseFragment<VB : ViewDataBinding>(@LayoutRes layoutId: Int = 0) : Fragment() {

    open lateinit var mRefreshLayout: SmartRefreshLayout
    open lateinit var mMultiplyStateView: MultiplyStateView

    //子类的布局id
    private val mLayoutId: Int = layoutId

    //子View的dataBinding
    lateinit var mBinding: VB

    lateinit var mRootView: View

    override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
    ): View? {
      //设置根布局
      mRootView = layoutInflater.inflate(R.layout.base_load_more_layout, container, false)
      return mRootView
    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      //初始化视图
      initView()
      //初始化数据
      initData()
    }

    //初始化视图
    open fun initView() {
      //获取加载成功View的DataViewBinding
      mBinding = DataBindingUtil.inflate(layoutInflater, mLayoutId, null, false)
      //找到根布局的baseFrameLayout
      mMultiplyStateView = mRootView.findViewById(R.id.multiply_state_view)
      //将子类加载成功View布局添加进去
      mMultiplyStateView.setSuccessView(mBinding.root)
      //获取到刷新框架
      mRefreshLayout = mRootView.findViewById(R.id.refreshLayout)
    }

    //初始化数据
    abstract fun initData()
}





/**
* @description: 携带ViewModel的fragment
* @author YL Chen
* @date 2024/9/6 21:52
* @version 1.0
*/
abstract class BaseVMFragment<VB : ViewDataBinding, VM : BaseViewModel>(@LayoutRes layoutId: Int) :
    BaseFragment<VB>(layoutId) {


    lateinit var mViewModel: VM

    override fun initData() {
      mViewModel = getViewModel()!!
      val variableId = getVariableId()
      if (variableId != -1) {
            mBinding.setVariable(getVariableId(), mViewModel)
            mBinding.executePendingBindings()
      }
      initState()
      initVMData()
      observeLiveData()
      //设置状态页点击重新加载监听
      mMultiplyStateView.setOnReLodListener(mViewModel)
    }

    /**
   * 获取子类xml 的Variable
   * @return Int
   */
    open fun getVariableId(): Int {
      return -1
    }

    /**
   * 初始化状态
   */
    private fun initState() {
      mViewModel.mStateViewLiveData.observe(this) {
            when (it) {
                ViewStateEnum.VIEW_LOADING -> {
                  LogUtils.d(this, "StateLayoutEnum.DATA_LOADING")
                  dataLoading()
                }

                ViewStateEnum.VIEW_EMPTY -> {
                  LogUtils.d(this, "StateLayoutEnum.DATA_ERROR")
                  dataEmpty()
                }


                ViewStateEnum.VIEW_NET_ERROR -> {
                  LogUtils.d(this, "StateLayoutEnum.NET_ERROR")
                  netError()
                }

                ViewStateEnum.VIEW_LOAD_SUCCESS -> {
                  LogUtils.d(this, "StateLayoutEnum.LOAD_SUCCESS")
                  loadSuccess()
                }

                ViewStateEnum.VIEW_NONE -> {
                  LogUtils.d(this, "StateLayoutEnum.NONE")
                }
            }
      }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      LogUtils.d(this, "onViewCreated")
    }

    /**
   * 数据加载成功
   */
    open fun loadSuccess() {
      mMultiplyStateView.showSuccess()
    }

    /**
   * 网络加载失败
   */
    open fun netError() {
      mMultiplyStateView.showNetError()
    }


    /**
   * 数据加载错误
   */
    open fun dataEmpty() {
      mMultiplyStateView.showEmpty()
    }

    /**
   * 数据加载中
   */
    open fun dataLoading() {
      mMultiplyStateView.showLoading()
      val loadingView = mMultiplyStateView.getLoadingView()
      val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
      myLoadingView.startRotate()
    }

    /**
   * 通过反射获取子类的ViewModel
   * @return VM?
   */
    private fun getViewModel(): VM? {//这里获得到的是类的泛型的类型
      val type = javaClass.genericSuperclass
      if (type != null && type is ParameterizedType) {
            val actualTypeArguments = type.actualTypeArguments
            val tClass = actualTypeArguments
            return ViewModelProvider(
                this,
                ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().application)
            )
                .get(tClass as Class<VM>)
      }
      return null
    }

    /**
   * 初始化ViewModel数据
   */
    abstract fun initVMData()

    /**
   * 监听ViewModel中的LiveData
   */
    open fun observeLiveData() {
    }

} 思路:

BaseXXX:

通过泛型吸收子类视图的ViewDataBInding类型,并通过构造方法获取子类的视图ID。
在onCreate()方法中,设置底子视图base_load_more_layout.xml,base_load_more_layout.xml中携带SmartRefreshLayout刷新框架和自定义的MultiplyStateView状态视图切换View,以实现刷新、加载更多视图和底子的状态视图切换功能;调用initView(),initData()方法
initView(): 初始化ViewDataBinding,使用构造方法中转达过来的子类视图ID举行初始化,并将其声明为公开的成员变量(mBinding),子类可直接调用,此ViewDataBinding为成功视图,并初始化SmartRefreshLayout和MultiplyStateView,声明为成员变量(mRefreshLayout和mMultiplyStateView),设置初始化的ViewDataBinding.root为MultiplyStateView的成功视图。
initData抽象方法欺压子类重写,用于初始化数据。
BaseVMXXX:

通过泛型担当子类的ViewDataBinding和ViewModel,并通过构造方法获取子类视图ID,继续自BaseXXX,将泛型ViewDataBinding和视图ID转达给BaseXXX。
重写BaseXXX的initData()方法:

[*] 通过反射获取通过泛型转达进来的子类ViewModel的对象,并将其声明为公开的成员变量,子类可直接调用(mViewModel)
[*] 调用getVariableId()方法获取子类转达过来的在XML结构中绑定的ViewModel变量,并通过mBinding调用setsetVariable()方法将其与mViewModel绑定到一起,使得可以在子类的视图的一些事件可以绑定对应的ViewModel中。
[*] 调用initViewState()初始化当前View的视图状态,默认是Loding(加载中)状态。
[*] 调用initVMData()初始化ViewModel数据
[*] 调用observeLiveData()监听ViewMode中的LiveData
[*] lifecycle.addObserver(mViewModel),使ViewModel监听对应View类的生命周期,相当于将ViewModel与View类举行绑定,使其只在View生命周期内相应,制止内存泄漏大概资源的错误开释。
[*] mMultiplyStateView.setOnReLodListener(mViewModel):为状态视图设置重新加载监听,监听者为View类对应的ViewModel。
getVariableId():公开的方法,默认返回-1,供子类重写返回XML视图中绑定的ViewModel变量
initViewState():观察对应ViewModel中的切换视图LiveData变量(mStateViewLiveData),实现状态视图的初始化和切换。
observeLiveData():公开的方法,供子类重写监听观察对应ViewMode中的LiveData变量。
补充:

自定义多状态视图View代码:

package com.yl.wanandroid.ui.custom

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.LayoutRes
import com.yl.wanandroid.R
import com.yl.wanandroid.model.ViewStateEnum
import com.yl.wanandroid.utils.LogUtils


/**
* @description: 自定义多状态View
* @author YL Chen
* @date 2024/9/29 10:47
* @version 1.0
*/

open class MultiplyStateView : FrameLayout {

    private var mOnReLodListener: OnReLodListener? = null
    private lateinit var params: LayoutParams
    private lateinit var mInflater: LayoutInflater
    private var mSuccessViewId: Int = 0
    private var mEmptyViewId: Int = 0
    private var mNetErrorViewId: Int = 0
    private var mLoadingViewId: Int = 0

    //四种展示的view
    private var mLoadingView: View? = null
    private var mSuccessView: View? = null
    private var mNetErrorView: View? = null
    private var mEmptyView: View? = null

    //当前视图状态
    private var currentState: ViewStateEnum = ViewStateEnum.VIEW_NONE

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defy: Int) : super(context, attrs, defy) {
      initView(context, attrs)
    }

    private fun initView(context: Context, attrs: AttributeSet?) {
      //获取自定义属性
      val a =
            context.obtainStyledAttributes(attrs, R.styleable.MultiplyStateView)
      mLoadingViewId =
            a.getResourceId(R.styleable.MultiplyStateView_msv_loadingView, R.layout.view_loading)
      mNetErrorViewId =
            a.getResourceId(R.styleable.MultiplyStateView_msv_netErrorView, R.layout.view_net_error)
      mEmptyViewId =
            a.getResourceId(R.styleable.MultiplyStateView_msv_emptyView, R.layout.view_empty)
      mSuccessViewId = a.getResourceId(R.styleable.MultiplyStateView_msv_successView, 0)

      a.recycle()
      mInflater = LayoutInflater.from(context)
      params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
      setLoadingView(mLoadingViewId)
      setEmptyView(mEmptyViewId)
      setNetErrorView(mNetErrorViewId)
    }

    //在 XML 布局文件中的视图被加载并且所有的子视图都被添加到父视图中之后执行
/*    override fun onFinishInflate() {
      super.onFinishInflate()
      //展示加载页面
      LogUtils.d(this,"onFinishInflate-->showLoading")
      showLoading()
      val loadingView = getLoadingView()
      val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
      myLoadingView.startRotate()
    }*/

    //++++++++++++++++++++++++++++++++加载页面++++++++++++++++++++

    //动态加载并展示加载页面
    fun showLoading() {
      if (mLoadingView == null) {
            mLoadingView = mInflater.inflate(mLoadingViewId, null)
      }
      if (mLoadingView != null ) {
            removeAllViews()
            addView(mLoadingView, 0, params)
      } else {
            throw NullPointerException("you have to set loading view before that")
      }
    }

    /**
   * 提供方法给外界设置自定义加载页面
   * @param layoutId Int 布局Id
   */
    open fun setLoadingView(@LayoutRes layoutId: Int) {
      setLoadingView(mInflater.inflate(layoutId, null))
    }

    open fun setLoadingView(view: View) {
      mLoadingView = view
    }

    /**
   * 获取加载页面
   */
    fun getLoadingView(): View {
      if (null == mLoadingView) {
            mLoadingView = mInflater.inflate(mLoadingViewId, null)
      }
      return mLoadingView!!
    }

    //++++++++++++++++++++++++++++++++成功页面++++++++++++++++++++
    /**
   * 显示成功状态
   */
    fun showSuccess() {
      if (null == mSuccessView) {
            mSuccessView = mInflater.inflate(mSuccessViewId, null)
      }
      if (mSuccessView != null) {
            LogUtils.d(this,"childCount-->${childCount}")
            val loadingView = getLoadingView()
            val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
            myLoadingView.stopRotate()
            removeAllViews()
            LogUtils.d(this,"childCount-->${childCount}")
            addView(mSuccessView, 0, params)
            LogUtils.d(this,"childCount-->${childCount}")
            currentState = ViewStateEnum.VIEW_LOAD_SUCCESS
      } else {
            throw NullPointerException("you have to set success view before that")
      }
    }

    /**
   * 设置自定义的成功页面
   *
   * @param layoutResID
   */
    fun setSuccessView(@LayoutRes layoutResID: Int) {
      setSuccessView(mInflater.inflate(layoutResID, null))
    }

    /**
   * 设置自定义的成功页面
   *
   * @param view
   */
    fun setSuccessView(view: View) {
      mSuccessView = view
      LogUtils.d(this,"setSuccessView-->${view}")
    }


    /**
   * 获取成功页面
   */
    fun getSuccessView(): View {
      if (null == mSuccessView) {
            mSuccessView = mInflater.inflate(mSuccessViewId, null)
      }
      return mSuccessView!!
    }


    //++++++++++++++++++++++++++++++++网络错误页面++++++++++++++++++++
    /**
   * 显示加载失败(网络错误)状态 带监听器的
   */
    fun showNetError() {
      if (null == mNetErrorView) {
            mNetErrorView = mInflater.inflate(mNetErrorViewId, null)
      }

      if (mNetErrorView != null) {
            removeAllViews()
            addView(mNetErrorView, 0, params)
            currentState = ViewStateEnum.VIEW_NET_ERROR
            mNetErrorView!!.setOnClickListener { showReLoading() }
      } else {
            throw java.lang.NullPointerException("you have to set unknown view before that")
      }
    }


    /**
   * 设置自定义的网络异常
   *
   * @param layoutResID
   */
    fun setNetErrorView(@LayoutRes layoutResID: Int) {
      setNetErrorView(mInflater.inflate(layoutResID, null))
    }

    /**
   * 设置自定义的网络异常
   *
   * @param view
   */
    fun setNetErrorView(view: View) {
      mNetErrorView = view
    }


    /**
   * 设置获取网络错误页面
   */
    fun getNetErrorView(): View {
      if (null == mNetErrorView) {
            mNetErrorView = mInflater.inflate(mNetErrorViewId, null)
      }
      return mNetErrorView!!
    }


    //++++++++++++++++++++++++++++++++空页面页面++++++++++++++++++++
    /**
   * 显示无数据状态
   */
    fun showEmpty() {
      if (null == mEmptyView) {
            mEmptyView = mInflater.inflate(mEmptyViewId, null)
      }

      if (mEmptyView != null) {
            removeAllViews()
            addView(mEmptyView, 0, params)
            currentState = ViewStateEnum.VIEW_EMPTY
      } else {
            throw java.lang.NullPointerException("you have to set empty view before that")
      }
    }


    /**
   * 设置自定义的空页面
   *
   * @param layoutResID
   */
    fun setEmptyView(@LayoutRes layoutResID: Int) {
      setEmptyView(mInflater.inflate(layoutResID, null))
    }

    /**
   * 设置自定义的空页面
   *
   * @param view
   */
    fun setEmptyView(view: View) {
      mEmptyView = view
    }


    /**
   * 设置获取空页面
   */
    fun getEmptyView(): View {
      if (null == mEmptyView) {
            mEmptyView = mInflater.inflate(mEmptyViewId, null)
      }
      return mEmptyView!!
    }


    /**
   * 再次加载数据
   */
    private fun showReLoading() {
      //第一步重新loading
      if (mOnReLodListener != null) {
            showLoading()
            mOnReLodListener!!.onReLoad()
      } else {
            //未设置重新加载回调
            LogUtils.e(this, "请设置重新加载监听")
      }
    }

    /**
   * 外部回调
   *
   * @param onReLodListener
   */
    fun setOnReLodListener(onReLodListener: OnReLodListener) {
      this.mOnReLodListener = onReLodListener
    }

    /**
   * 重新加载页面的回调接口
   */
    interface OnReLodListener {
      fun onReLoad()
    }

    override fun onAttachedToWindow() {
      super.onAttachedToWindow()
      updateVisibility()
    }

    private fun updateVisibility() {
      // 获取父布局的可见性
      val parentVisibility = (parent as? View)?.visibility ?: View.VISIBLE
      visibility = parentVisibility
    }


    // 如果需要监听父布局的可见性变化,可以重写这个方法
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
      super.onLayout(changed, left, top, right, bottom)
      updateVisibility()
    }

    override fun onDetachedFromWindow() {
      super.onDetachedFromWindow()
      mOnReLodListener = null // 清除监听器引用
    }

} ViewModel层

BaseViewModel详细代码:

/**
* @description: ViewModel基类
* @author YL Chen
* @date 2024/9/6 14:17
* @version 1.0
*/
abstract class BaseViewModel : ViewModel(),MultiplyStateView.OnReLodListener {

    /**
   * 控制状态视图的LiveData
   */
    val mStateViewLiveData = MutableLiveData(ViewStateEnum.VIEW_LOADING)

    /**
   * 切换到UI线程
   * @param errorCallback SuspendFunction0<Unit> 错误回调
   * @param requestCall SuspendFunction0<Unit> 网络请求函数
   */
    fun launchUI(
      errorCallback: suspend (Int?, String?) -> Unit,
      requestCall: suspend () -> Unit
    ) {
      viewModelScope.launch(Dispatchers.Main) {
            //统一进行异常捕获
            safeApiCall(errorCallback, requestCall)
      }
    }

    /**
   * 对网络请求进行统一异常捕获
   * @param errorCallback SuspendFunction2<Int?, String?, Unit> 错误回调
   * @param requestCall SuspendFunction0<Unit> 网络请求函数
   * @return T? 网络请求成功数据
   */
    private suspend fun<T> safeApiCall(
      errorCallback: suspend (Int?, String?) -> Unit,
      requestCall: suspend () -> T?
    ): T? {
      try {
            //返回网络请求结果
            return requestCall()
      }catch (e: Exception){
            LogUtils.e(this@BaseViewModel,e.message.toString())
            e.printStackTrace()
            //统一异常处理
            //将异常转为ApiException
            val apiException = ExceptionHandler.handleException(e)
            if (apiException.errCode == ERROR.UNKNOW_HOST.code || apiException.errCode == ERROR.NETWORD_ERROR.code){
                changeStateView(ViewStateEnum.VIEW_NET_ERROR)
            }
            errorCallback(apiException.errCode,apiException.errMsg)
      }
      return null
    }

    /**
   * 更改状态视图的状态
   */
    fun changeStateView(
      state: ViewStateEnum
    ) {
      // 对参数进行校验
      when (state) {
            ViewStateEnum.VIEW_LOADING -> {
                mStateViewLiveData.postValue(ViewStateEnum.VIEW_LOADING)
            }

            ViewStateEnum.VIEW_EMPTY -> {
                mStateViewLiveData.postValue(ViewStateEnum.VIEW_EMPTY)
            }

            ViewStateEnum.VIEW_NET_ERROR -> {
                mStateViewLiveData.postValue(ViewStateEnum.VIEW_NET_ERROR)
            }

            ViewStateEnum.VIEW_LOAD_SUCCESS -> {
                mStateViewLiveData.postValue(ViewStateEnum.VIEW_LOAD_SUCCESS)
            }
            ViewStateEnum.VIEW_NONE -> {
                mStateViewLiveData.postValue(ViewStateEnum.VIEW_NONE)
            }
      }
    }

    //错误视图点击回调函数
    override fun onReLoad() {
      //调用方法由子类实现
      onReload()
    }

    //子类可实现此方法实现界面重新加载
    open fun onReload(){}
} 思路:

继续自ViewModel,实现MultiplyStateView.OnReLodListener接口,重写onReload()方法,当用户点击重新加载状态视图时,回调此方法
声明mStateViewLiveData成员变量,给View层观察监听
封装changeStateView()方法,用于在网络哀求数据相干操纵后举行手动调用,改变mStateViewLiveData变量的值,View下层对此变量举行观察监听,实现状态视图的实时切换。
onReload()方法:公开的方法,用户自己选择是否重新,不是MultiplyStateView.OnReLodListener欺压重写的,而是自定义的,在欺压重写的onReload中调用。
封装safeApiCall()方法,传入网络哀求函数和错误回调函数,用于调用Modle层中的网络哀求方法,并对返回结果举行统一的异常处理,如当网络异常时,调用ViewModel.changStateView(ViewStateEnum.VIEW_NET_ERROR)改变状态视图。
封装launchUI()方法,用于切换到UI线程,在其中调用safeApiCall()方法。
Model层

BaseBean详细代码:网络哀求返回的底子数据Bean类(与接口返回的数据结构有关)

data class BaseBean<T>(val data: T,val errorCode: Int, val errorMsg: String)/**
* 网络返回数据类
* @param T
* @property errorCode String 0:正常,非0异常
* @property errorMsg String
* @property data T
* @constructor
*/

data class BaseResult<T>(val data: T,val errorCode: Int, val errorMsg: String, ) {
    fun isFailed(): Boolean {
      return errorCode != 0
    }
} BaseRepository详细代码:网络哀求底子仓库

/**
* @description: 网络请求基础仓库
* @author YL Chen
* @date 2024/9/10 17:06
* @version 1.0
*/
open class BaseRepository {
    /**
   * IO中处理请求,请求错误抛出自定义异常
   * @param requestCall SuspendFunction0<BaseResult<T>?>
   * @return T?
   */
    suspend fun <T> requestResponse(requestCall: suspend () -> BaseResult<T>?): T? {
      val result = withContext(Dispatchers.IO) {
            withTimeout(Constant.CONNECT_TIME_OUT * 1000) {
                requestCall()
            }
      } ?: return null
      LogUtils.e(this@BaseRepository,"result-->$result")

      if (result.isFailed()) {
            throw ApiException(result.errorCode, result.errorMsg)
      }
      return result.data
    }
}


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android项目实战搭建 MVVM架构