refactor(frame): 优化基类,添加请求状态的支持

This commit is contained in:
Quyunshuo
2021-07-11 18:12:06 +08:00
parent 7429a02189
commit dddd7f5b54
24 changed files with 183 additions and 208 deletions

View File

@@ -1,6 +1,7 @@
package com.quyunshuo.androidbaseframemvvm.base.mvvm.m
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
@@ -15,9 +16,12 @@ open class BaseRepository {
/**
* 发起请求封装
* 该方法将flow的执行切换至IO线程
*
* @param requestBlock 请求的整体逻辑
* @return Flow<T>
*/
protected fun <T> flowRequest(requestBlock: suspend FlowCollector<T>.() -> Unit) =
flow(block = requestBlock).flowOn(Dispatchers.IO) // 通过 flowOn 切换到 io 线程
protected fun <T> request(requestBlock: suspend FlowCollector<T>.() -> Unit): Flow<T> {
return flow(block = requestBlock).flowOn(Dispatchers.IO)
}
}

View File

@@ -2,8 +2,12 @@ package com.quyunshuo.androidbaseframemvvm.base.mvvm.v
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.launcher.ARouter
import com.jaeger.library.StatusBarUtil
import com.quyunshuo.androidbaseframemvvm.base.R
import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel
import com.quyunshuo.androidbaseframemvvm.base.utils.BindingReflex
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusRegister
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusUtils
@@ -15,12 +19,15 @@ import com.quyunshuo.androidbaseframemvvm.base.utils.ViewRecreateHelper
* @author Qu Yunshuo
* @since 8/27/20
*/
abstract class BaseFrameActivity<VB : ViewBinding> : AppCompatActivity(), FrameView<VB> {
abstract class BaseFrameActivity<VB : ViewBinding, VM : BaseViewModel> : AppCompatActivity(),
FrameView<VB> {
protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) {
BindingReflex.reflexViewBinding(javaClass, layoutInflater)
}
protected abstract val mViewModel: VM
/**
* activity页面重建帮助类
*/
@@ -35,17 +42,20 @@ abstract class BaseFrameActivity<VB : ViewBinding> : AppCompatActivity(), FrameV
ARouter.getInstance().inject(this)
// 注册EventBus
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.register(this)
setStatusBar()
mBinding.initView()
initLiveDataObserve()
initObserve()
initRequestData()
}
override fun onDestroy() {
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.unRegister(
this
)
super.onDestroy()
}
/**
* 设置状态栏
* 子类需要自定义时重写该方法即可
* @return Unit
*/
open fun setStatusBar() {}
override fun isRecreate(): Boolean = mStatusHelper?.isRecreate ?: false
@@ -62,6 +72,13 @@ abstract class BaseFrameActivity<VB : ViewBinding> : AppCompatActivity(), FrameV
/**
* - activity 重建帮助工具类
*/
private class ActivityRecreateHelper(savedInstanceState: Bundle? = null) : ViewRecreateHelper(savedInstanceState)
private class ActivityRecreateHelper(savedInstanceState: Bundle? = null) :
ViewRecreateHelper(savedInstanceState)
override fun onDestroy() {
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.unRegister(
this
)
super.onDestroy()
}
}

View File

@@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.launcher.ARouter
import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel
import com.quyunshuo.androidbaseframemvvm.base.utils.BindingReflex
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusRegister
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusUtils
@@ -18,12 +19,14 @@ import com.quyunshuo.androidbaseframemvvm.base.utils.ViewRecreateHelper
* @author Qu Yunshuo
* @since 8/27/20
*/
abstract class BaseFrameFragment<VB : ViewBinding> : Fragment(), FrameView<VB> {
abstract class BaseFrameFragment<VB : ViewBinding, VM : BaseViewModel> : Fragment(), FrameView<VB> {
protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) {
BindingReflex.reflexViewBinding(javaClass, layoutInflater)
}
protected abstract val mViewModel: VM
/**
* fragment状态保存工具类
*/
@@ -45,8 +48,9 @@ abstract class BaseFrameFragment<VB : ViewBinding> : Fragment(), FrameView<VB> {
ARouter.getInstance().inject(this)
// 注册EventBus
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.register(this)
mBinding.initView()
initLiveDataObserve()
initObserve()
initRequestData()
}
@@ -65,16 +69,17 @@ abstract class BaseFrameFragment<VB : ViewBinding> : Fragment(), FrameView<VB> {
super.onSaveInstanceState(outState)
}
/**
* - fragment状态保存帮助类
* - 暂时没有其他需要保存的--空继承
*/
private class FragmentStatusHelper(savedInstanceState: Bundle? = null) :
ViewRecreateHelper(savedInstanceState)
override fun onDestroy() {
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.unRegister(
this
)
super.onDestroy()
}
/**
* - fragment状态保存帮助类
* - 暂时没有其他需要保存的--空继承
*/
private class FragmentStatusHelper(savedInstanceState: Bundle? = null) : ViewRecreateHelper(savedInstanceState)
}

View File

@@ -1,40 +0,0 @@
package com.quyunshuo.androidbaseframemvvm.base.mvvm.v
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.launcher.ARouter
import com.quyunshuo.androidbaseframemvvm.base.utils.BindingReflex
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusRegister
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusUtils
/**
* 不使用 MVVM 的 Activity 基类
*
* @author Qu Yunshuo
* @since /9/10/20
*/
abstract class BaseFrameNotMVVMActivity<VB : ViewBinding> : AppCompatActivity(),
FrameNotMVVMView<VB> {
protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) {
BindingReflex.reflexViewBinding(javaClass, layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
// ARouter 依赖注入
ARouter.getInstance().inject(this)
// 注册EventBus
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.register(this)
mBinding.initView()
}
override fun onDestroy() {
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.unRegister(
this
)
super.onDestroy()
}
}

View File

@@ -1,49 +0,0 @@
package com.quyunshuo.androidbaseframemvvm.base.mvvm.v
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.launcher.ARouter
import com.quyunshuo.androidbaseframemvvm.base.utils.BindingReflex
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusRegister
import com.quyunshuo.androidbaseframemvvm.base.utils.EventBusUtils
/**
* 不使用 MVVM 的 Fragment 基类
*
* @author Qu Yunshuo
* @since 9/10/20
*/
abstract class BaseFrameNotMVVMFragment<VB : ViewBinding> : Fragment(), FrameNotMVVMView<VB> {
protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) {
BindingReflex.reflexViewBinding(javaClass, layoutInflater)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ARouter 依赖注入
ARouter.getInstance().inject(this)
// 注册EventBus
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.register(this)
mBinding.initView()
}
override fun onDestroy() {
if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) EventBusUtils.unRegister(
this
)
super.onDestroy()
}
}

View File

@@ -1,16 +0,0 @@
package com.quyunshuo.androidbaseframemvvm.base.mvvm.v
import androidx.viewbinding.ViewBinding
/**
* View层基类抽象
*
* @author Qu Yunshuo
* @since 10/13/20
*/
interface FrameNotMVVMView<VB : ViewBinding> {
/**
* 初始化View
*/
fun VB.initView()
}

View File

@@ -9,18 +9,19 @@ import androidx.viewbinding.ViewBinding
* @since 10/13/20
*/
interface FrameView<VB : ViewBinding> {
/**
* 初始化View
*/
fun VB.initView()
/**
* 初始化LiveData的订阅关系
* 订阅LiveData
*/
fun initLiveDataObserve()
fun initObserve()
/**
* 初始化界面创建时的数据请求,尝试在此方法内调用[isRecreate]进行重建判断,防止数据重复获取
* 用于在页面创建时进行请求接口
*/
fun initRequestData()

View File

@@ -2,6 +2,8 @@ package com.quyunshuo.androidbaseframemvvm.base.mvvm.vm
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.quyunshuo.androidbaseframemvvm.base.utils.StateLayoutEnum
import kotlin.jvm.Throws
/**
* ViewModel 基类
@@ -11,9 +13,45 @@ import androidx.lifecycle.ViewModel
*/
abstract class BaseViewModel : ViewModel() {
// Loading 状态
val isLoading = MutableLiveData(false)
/**
* 控制状态视图的LiveData
*/
val stateViewLD = MutableLiveData<StateLayoutEnum>()
// 请求异常
val requestError = MutableLiveData<Throwable?>()
/**
* 更改状态视图的状态
*
* @param hide Boolean 是否进行隐藏状态视图
* @param loading Boolean 是否显示加载中视图
* @param error Boolean 是否显示错误视图
* @param noData Boolean 是否显示没有数据视图
* @return Unit
* @throws IllegalArgumentException 如果入参没有传入任何参数或者为true的参数 >1 时,会抛出[IllegalArgumentException]
*/
@Throws(IllegalArgumentException::class)
protected fun changeStateView(
hide: Boolean = false,
loading: Boolean = false,
error: Boolean = false,
noData: Boolean = false
) {
// 对参数进行校验
var count = 0
if (hide) count++
if (loading) count++
if (error) count++
if (noData) count++
when {
count == 0 -> throw IllegalArgumentException("必须设置一个参数为true")
count > 1 -> throw IllegalArgumentException("只能有一个参数为true")
}
// 修改状态
when {
hide -> stateViewLD.postValue(StateLayoutEnum.HIDE)
loading -> stateViewLD.postValue(StateLayoutEnum.LOADING)
error -> stateViewLD.postValue(StateLayoutEnum.ERROR)
noData -> stateViewLD.postValue(StateLayoutEnum.NO_DATA)
}
}
}

View File

@@ -0,0 +1,13 @@
package com.quyunshuo.androidbaseframemvvm.base.mvvm.vm
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
/**
* 空的ViewModel 主要给现阶段不需要ViewModel的界面使用
*
* @author Qu Yunshuo
* @since 2021/7/10 11:04 上午
*/
@HiltViewModel
class EmptyViewModel @Inject constructor() : BaseViewModel()

View File

@@ -0,0 +1,14 @@
package com.quyunshuo.androidbaseframemvvm.base.utils
/**
* 状态视图的状态枚举
*
* @author Qu Yunshuo
* @since 2021/7/10 9:16 上午
*/
enum class StateLayoutEnum {
HIDE, // 隐藏
LOADING, // 加载中
ERROR, // 错误
NO_DATA // 没有数据
}

View File

@@ -1,8 +1,14 @@
package com.quyunshuo.androidbaseframemvvm.common.ui
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.viewbinding.ViewBinding
import com.jaeger.library.StatusBarUtil
import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameActivity
import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel
import com.quyunshuo.androidbaseframemvvm.base.utils.ActivityStackManager
import com.quyunshuo.androidbaseframemvvm.base.utils.AndroidBugFixUtils
import com.quyunshuo.androidbaseframemvvm.common.R
/**
* Activity基类
@@ -10,7 +16,22 @@ import com.quyunshuo.androidbaseframemvvm.base.utils.AndroidBugFixUtils
* @author Qu Yunshuo
* @since 8/27/20
*/
abstract class BaseActivity<VB : ViewBinding> : BaseFrameActivity<VB>() {
abstract class BaseActivity<VB : ViewBinding, VM : BaseViewModel> : BaseFrameActivity<VB, VM>() {
/**
* 设置状态栏
* 子类需要自定义时重写该方法即可
* @return Unit
*/
override fun setStatusBar() {
val themeColor = ContextCompat.getColor(this, R.color.common_theme)
StatusBarUtil.setColor(this, themeColor, 0)
}
override fun onResume() {
super.onResume()
Log.d("ActivityLifecycle", "ActivityStack: ${ActivityStackManager.activityStack}")
}
override fun onDestroy() {
super.onDestroy()

View File

@@ -2,6 +2,7 @@ package com.quyunshuo.androidbaseframemvvm.common.ui
import androidx.viewbinding.ViewBinding
import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameFragment
import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel
/**
* Fragment基类
@@ -9,4 +10,4 @@ import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameFragment
* @author Qu Yunshuo
* @since 8/27/20
*/
abstract class BaseFragment<VB : ViewBinding> : BaseFrameFragment<VB>()
abstract class BaseFragment<VB : ViewBinding, VM : BaseViewModel> : BaseFrameFragment<VB, VM>()

View File

@@ -1,20 +0,0 @@
package com.quyunshuo.androidbaseframemvvm.common.ui
import androidx.viewbinding.ViewBinding
import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameNotMVVMActivity
import com.quyunshuo.androidbaseframemvvm.base.utils.AndroidBugFixUtils
/**
* 不是 MVVM 模式的基类
*
* @author Qu Yunshuo
* @since 9/10/20
*/
abstract class BaseNotMVVMActivity<VB : ViewBinding> : BaseFrameNotMVVMActivity<VB>() {
override fun onDestroy() {
// 解决某些特定机型会触发的Android本身的Bug
AndroidBugFixUtils().fixSoftInputLeaks(this)
super.onDestroy()
}
}

View File

@@ -1,12 +0,0 @@
package com.quyunshuo.androidbaseframemvvm.common.ui
import androidx.viewbinding.ViewBinding
import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameNotMVVMFragment
/**
* 不是 MVVM 模式的基类
*
* @author Qu Yunshuo
* @since 9/10/20
*/
abstract class BaseNotMVVMFragment<VB : ViewBinding> : BaseFrameNotMVVMFragment<VB>()

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 主题色 -->
<color name="common_theme">#00BCD4</color>
</resources>

View File

@@ -4,7 +4,7 @@
<application>
<!-- Main 首页 -->
<activity android:name=".MainActivity">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -1,4 +1,4 @@
package com.quyunshuo.module.home
package com.quyunshuo.module.home.activity
import com.quyunshuo.androidbaseframemvvm.base.mvvm.m.BaseRepository
import com.quyunshuo.module.home.net.HomeApiService
@@ -19,7 +19,7 @@ class HomeRepository @Inject constructor() : BaseRepository() {
/**
* 模拟获取数据
*/
suspend fun getData() = flowRequest<String> {
suspend fun getData() = request<String> {
delay(1000L)
emit("Hello Hilt")
}

View File

@@ -1,4 +1,4 @@
package com.quyunshuo.module.home
package com.quyunshuo.module.home.activity
import android.util.Log
import androidx.lifecycle.MutableLiveData

View File

@@ -1,7 +1,9 @@
package com.quyunshuo.module.home.activity
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.EmptyViewModel
import com.quyunshuo.androidbaseframemvvm.common.ui.BaseActivity
import com.quyunshuo.module.home.databinding.HomeActivityInternalLayoutBinding
import com.quyunshuo.module.home.fragment.InternalFragment
@@ -12,7 +14,9 @@ import dagger.hilt.android.AndroidEntryPoint
* - 文件描述 : ViewPager2+fragment 模拟Fragment页面重建。
*/
@AndroidEntryPoint
class InternalPagerActivity : BaseActivity<HomeActivityInternalLayoutBinding>() {
class InternalPagerActivity : BaseActivity<HomeActivityInternalLayoutBinding, EmptyViewModel>() {
override val mViewModel: EmptyViewModel by viewModels()
override fun HomeActivityInternalLayoutBinding.initView() {
initPager()
@@ -35,12 +39,7 @@ class InternalPagerActivity : BaseActivity<HomeActivityInternalLayoutBinding>()
}
override fun initLiveDataObserve() {
}
override fun initRequestData() {
}
override fun initObserve() {}
override fun initRequestData() {}
}

View File

@@ -1,12 +1,10 @@
package com.quyunshuo.module.home
package com.quyunshuo.module.home.activity
import android.content.Intent
import android.graphics.Color
import androidx.activity.viewModels
import com.quyunshuo.androidbaseframemvvm.common.ui.BaseActivity
import com.quyunshuo.module.home.activity.InternalPagerActivity
import com.quyunshuo.module.home.databinding.HomeActivityMainBinding
import com.quyunshuo.module.home.fragment.InternalFragment
import dagger.hilt.android.AndroidEntryPoint
/**
@@ -16,12 +14,12 @@ import dagger.hilt.android.AndroidEntryPoint
* @since 5/22/21 2:26 PM
*/
@AndroidEntryPoint
class MainActivity : BaseActivity<HomeActivityMainBinding>() {
class MainActivity : BaseActivity<HomeActivityMainBinding, HomeViewModel>() {
/**
* 通过 viewModels() + Hilt 获取 ViewModel 实例
*/
private val mViewModel by viewModels<HomeViewModel>()
override val mViewModel by viewModels<HomeViewModel>()
override fun HomeActivityMainBinding.initView() {
goToNextBtn.setOnClickListener {
@@ -29,8 +27,7 @@ class MainActivity : BaseActivity<HomeActivityMainBinding>() {
}
}
override fun initLiveDataObserve() {
override fun initObserve() {
// 订阅数据
mViewModel.data.observe(this, {
mBinding.vTvHello.text = it

View File

@@ -10,28 +10,26 @@ import dagger.hilt.android.AndroidEntryPoint
* - 文件描述 : 测试fragment
*/
@AndroidEntryPoint
class InternalFragment : BaseFragment<HomeFragmentInternalLayoutBinding>() {
class InternalFragment : BaseFragment<HomeFragmentInternalLayoutBinding, InternalViewModel>() {
private val mViewModel by viewModels<InternalViewModel>()
override val mViewModel by viewModels<InternalViewModel>()
override fun HomeFragmentInternalLayoutBinding.initView() {
override fun HomeFragmentInternalLayoutBinding.initView() {}
}
override fun initLiveDataObserve() {
override fun initObserve() {
mViewModel.recreatedCont.observe(viewLifecycleOwner) {
mBinding.recreateContTv.text = "重建次数 $it"
}
mViewModel.firstData.observe(viewLifecycleOwner) {
mBinding.loadDataTv.text = it
}
mViewModel.isLoading.observe(viewLifecycleOwner) {
mBinding.loadingStatusTv.text = if (it) {
"正在加载..."
} else {
"加载完成!"
}
}
// mViewModel.isLoading.observe(viewLifecycleOwner) {
// mBinding.loadingStatusTv.text = if (it) {
// "正在加载..."
// } else {
// "加载完成!"
// }
// }
}
override fun initRequestData() {
@@ -43,6 +41,4 @@ class InternalFragment : BaseFragment<HomeFragmentInternalLayoutBinding>() {
}
mViewModel.getData()
}
}

View File

@@ -10,9 +10,8 @@ import javax.inject.Inject
*/
class InternalRepository @Inject constructor() : BaseRepository() {
suspend fun getData() = flowRequest<String> {
suspend fun getData() = request<String> {
delay(1000)
emit("数据加载成功")
}
}

View File

@@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -17,7 +18,8 @@ import javax.inject.Inject
* - 文件描述 :
*/
@HiltViewModel
class InternalViewModel @Inject constructor(private val repository: InternalRepository) : BaseViewModel() {
class InternalViewModel @Inject constructor(private val repository: InternalRepository) :
BaseViewModel() {
/**
* 重建计数
@@ -41,13 +43,14 @@ class InternalViewModel @Inject constructor(private val repository: InternalRepo
* 获取数据
*/
fun getData() {
isLoading.value = true
viewModelScope.launch(Dispatchers.IO) {
repository.getData()
.catch {
Log.d("DJC", "getData: ")
}.collect {
isLoading.postValue(false)
}
.onStart { changeStateView(loading = true) }
.collect {
changeStateView(hide = true)
delay(200)
firstData.postValue(it)
}
@@ -56,7 +59,6 @@ class InternalViewModel @Inject constructor(private val repository: InternalRepo
override fun onCleared() {
super.onCleared()
Log.d("DJC","InternalViewModel Clear")
Log.d("DJC", "InternalViewModel Clear")
}
}

View File

@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
tools:context=".activity.MainActivity">
<TextView
android:id="@+id/vTvHello"