package com.xw.common.base
|
|
import android.app.Application
|
import android.os.Bundle
|
import com.xw.common.utils.LogUtils
|
import android.view.View
|
import androidx.lifecycle.*
|
import com.blankj.utilcode.util.AppUtils
|
import com.xw.common.utils.MMKVManager
|
import com.xw.common.net.interceptor.ApiException
|
import com.xw.common.utils.ToastUtilCommon
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.launch
|
import org.koin.core.component.KoinComponent
|
import java.io.IOException
|
import java.net.ConnectException
|
import java.net.SocketException
|
import java.net.SocketTimeoutException
|
import java.net.UnknownHostException
|
import java.util.*
|
|
open class BaseViewModel(application: Application) : AndroidViewModel(application),
|
IBaseViewModel, KoinComponent {
|
|
var TAG = "zmc-${this.javaClass.simpleName}"
|
|
/**
|
* 协程全局异常处理器,防止未捕获异常导致崩溃
|
*/
|
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
LogUtils.e(TAG, "Uncaught coroutine exception: ${throwable.message}")
|
runOnMainThread {
|
ToastUtilCommon.showError(getNetworkErrorMessage(throwable))
|
}
|
}
|
|
var startActivityEvent: SingleLiveEvent<MutableMap<String, Any?>> = SingleLiveEvent()
|
var onBackEvent: SingleLiveEvent<String> = SingleLiveEvent()
|
var closeEvent: SingleLiveEvent<String> = SingleLiveEvent()
|
var dialogEvent: SingleLiveEvent<String> = SingleLiveEvent()
|
|
fun finish() {
|
onBackEvent.postValue("finish")
|
}
|
|
fun onBackPressed() {
|
onBackEvent.postValue("back")
|
}
|
|
// 当前正在显示的dialog数量
|
var currentDialogNum = 0
|
fun showDialog(content: String = "请稍后...") {
|
currentDialogNum++
|
dialogEvent.postValue(content)
|
}
|
|
fun dismissDialog() {
|
if (currentDialogNum > 0) currentDialogNum--
|
if (currentDialogNum == 0) {
|
dialogEvent.postValue("dismiss")
|
}
|
}
|
|
fun forceDismissDialog() {
|
LogUtils.e(TAG, "forceDismissDialog: $currentDialogNum")
|
currentDialogNum = 0
|
dialogEvent.postValue("dismiss")
|
}
|
|
fun launchRequest(
|
tips: String = "加载中...",
|
showLoading: Boolean = true,
|
error: suspend (code: Int, msg: String) -> Unit = { code, msg ->
|
ToastUtilCommon.showError(msg)
|
LogUtils.e(TAG, "launchRequest: code = $code msg = $msg", )
|
},
|
request: suspend () -> Unit
|
) {
|
if (showLoading) showDialog(tips)
|
viewModelScope.launch(Dispatchers.IO + exceptionHandler) {
|
try {
|
request()
|
} catch (e: Throwable) {
|
try {
|
errorHandling(e, error)
|
} catch (handlingError: Throwable) {
|
LogUtils.e(TAG, "Error handling failed: ${handlingError.message}")
|
}
|
} finally {
|
if (showLoading) dismissDialog()
|
}
|
}
|
}
|
|
|
fun <T> launchRequestFlow(
|
tips: String = "加载中...",
|
showLoading: Boolean = true,
|
error: suspend (code: Int, msg: String) -> Unit = { code, msg ->
|
ToastUtilCommon.showError(msg)
|
LogUtils.e(TAG, "launchRequestFlow: code = $code msg = $msg")
|
},
|
flowBlock: () -> Flow<T>,
|
onCollect: (T) -> Unit
|
) {
|
if (showLoading) showDialog(tips)
|
viewModelScope.launch(Dispatchers.IO + exceptionHandler) {
|
try {
|
flowBlock().collect { data ->
|
onCollect(data)
|
}
|
} catch (e: Throwable) {
|
try {
|
errorHandling(e, error)
|
} catch (handlingError: Throwable) {
|
LogUtils.e(TAG, "Error handling failed: ${handlingError.message}")
|
}
|
} finally {
|
if (showLoading) dismissDialog()
|
}
|
}
|
}
|
|
private suspend fun errorHandling(e: Throwable, error: suspend (code: Int, msg: String) -> Unit) {
|
if (e is ApiException) {
|
when {
|
e.isTokenExpired() -> {
|
MMKVManager.getInstance().clear()
|
ToastUtilCommon.showWarn("登录已过期,请重新登录")
|
viewModelScope.launch {
|
delay(1000)
|
AppUtils.relaunchApp(true)
|
}
|
}
|
|
else -> {
|
error.invoke(e.code, e.message ?: "")
|
}
|
}
|
} else {
|
val errorMsg = getNetworkErrorMessage(e)
|
error.invoke(-1, errorMsg)
|
}
|
}
|
|
/**
|
* 获取网络异常的友好提示信息
|
*/
|
private fun getNetworkErrorMessage(e: Throwable): String {
|
return when (e) {
|
is UnknownHostException -> "网络连接失败,请检查网络设置"
|
is ConnectException -> "无法连接到服务器,请检查网络"
|
is SocketTimeoutException -> "连接超时,请稍后重试"
|
is SocketException -> "网络异常,请检查网络连接"
|
is IOException -> "网络请求失败,请检查网络"
|
else -> e.message ?: "未知错误"
|
}
|
}
|
|
fun runOnMainThread(block: () -> Unit) {
|
viewModelScope.launch(Dispatchers.Main) {
|
block()
|
}
|
}
|
|
fun onBack(view: View) {
|
onBackPressed()
|
}
|
|
/**
|
* 需要登录的跳转页面
|
*
|
* @param clz 所跳转的目的Activity类
|
*/
|
open fun startActivityNeedLoad(clz: Class<*>?) {
|
startActivity(clz, null)
|
}
|
|
/**
|
* 跳转页面
|
*
|
* @param clz 所跳转的目的Activity类
|
*/
|
open fun startActivity(clz: Class<*>?) {
|
startActivity(clz, null)
|
}
|
|
/**
|
* 跳转页面
|
*
|
* @param clz 所跳转的目的Activity类
|
* @param bundle 跳转所携带的信息
|
*/
|
open fun startActivity(clz: Class<*>?, bundle: Bundle?) {
|
val params: MutableMap<String, Any?> = HashMap()
|
params["CLASS"] = clz
|
if (bundle != null) {
|
params["BUNDLE"] = bundle
|
}
|
startActivityEvent.postValue(params)
|
}
|
|
fun startActivityForResult(clz: Class<*>?, code: Int, bundle: Bundle? = null) {
|
val params: MutableMap<String, Any?> = HashMap()
|
params["CLASS"] = clz
|
params["requestCode"] = code
|
if (bundle != null) {
|
params["BUNDLE"] = bundle
|
}
|
startActivityEvent.postValue(params)
|
}
|
|
fun closeKeyboard() {
|
closeEvent.postValue("close")
|
}
|
|
fun hideKeyboard() {
|
closeEvent.postValue("hide")
|
}
|
|
override fun onCreate() {}
|
|
override fun onDestroy() {}
|
|
override fun onStart() {}
|
|
override fun onStop() {}
|
|
override fun onResume() {}
|
|
override fun onPause() {}
|
|
}
|