1.定义一个网络消息类
data class HttpState(
data class HttpState(
var show: Boolean = false,
var isLoading: Boolean = false,
/**
* 响应编码
*/
var code: String = "",
/**
* 响应的业务处理结果
*/
var success: Boolean = true,
/**
* 响应的消息
*/
val message: String? = null,
) {
companion object {
fun state(isLoading: Boolean, code: String, success: Boolean, message: String?): HttpState {
return HttpState(
show = true,
isLoading = isLoading,
code = code,
success = success,
message = message
)
}
fun fail(message: String): HttpState {
return HttpState(show = true, success = false, message = message)
}
}
}
2.定义一个基础viewModel 实现统一的网络方法请求
/**
* @author Sun
*/
abstract class UnitMBViewModel : ViewModel() {
val RED_WRITE_PERMISSIONS: MutableList<String> = mutableListOf()
init {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
RED_WRITE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE)
RED_WRITE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// RED_WRITE_PERMISSIONS.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
// }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RED_WRITE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES)
RED_WRITE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO)
RED_WRITE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO)
}
}
@Inject
lateinit var preferences: UnitMBPreferences
var httpState by mutableStateOf(HttpState())
var confirmState by mutableStateOf(UConfirmState())
var currentUser: UserInfo
get() {
val value = this.preferences.getString(UnitMBEnvironment.USER_INFO)
return Gson().fromJson(value, UserInfo::class.java)
}
set(value) {
this.preferences.putString(UnitMBEnvironment.USER_INFO, value.toString())
}
val token: String
get() {
return preferences.getString(UnitMBEnvironment.USER_TOKEN)
}
fun exitLogin(navController: NavController) {
preferences.clearData()
navController.navigate(Routes.Login.route)
}
fun closeHttpState() {
httpState = HttpState()
}
fun closeConfirm() {
confirmState = UConfirmState()
}
protected fun confirm(
type: UConfirmType = UConfirmType.PRIMARY,
title: String,
message: String,
confirm: () -> Unit = { }
) {
confirmState = UConfirmState.show(type, title, message, confirm)
}
/**
* 网络状态监听
*/
protected suspend fun <D> network(
loading: Boolean = true,
request: suspend () -> UnitMBResponse<D>,
): D? {
if (loading) {
httpState = HttpState.state(true, "0", true, "加载中")
}
return try {
val response = request()
httpState = HttpState.state(false, response.code, response.success, response.message)
response.data
} catch (e: Exception) {
e.printStackTrace()
if (e is ConnectException) {
httpState = HttpState.state(false, "-1", false, "网络请求异常")
} else if (e is HttpException) {
when (e.code()) {
401 -> httpState = HttpState.state(
false,
e.code().toString(),
false,
e.response()?.errorBody()?.string()
)
403 -> httpState = HttpState.state(
false,
HttpError.Forbidden.code,
false,
HttpError.Forbidden.message
)
404 -> httpState = HttpState.state(
false,
HttpError.NotFound.code,
false,
HttpError.NotFound.message
)
500 -> httpState = HttpState.state(
false, HttpError.ServerError.code, false, HttpError.ServerError.message
)
}
} else {
httpState = HttpState.state(false, "-1", false, "网络请求异常")
}
null
}
}
}
3.在业务viewModel使用
@HiltViewModel
class HomeViewModel @Inject constructor(
private val repository: HomeRepository
) : UnitMBViewModel() {
/**
*
*/
var searchValue by mutableStateOf("")
/**
* 用户信息
*/
var userInfo: UserInfo by mutableStateOf(UserInfo())
/**
* 获取用户信息
*/
suspend fun getUserInfo() {
val data = network(false) { repository.getUserInfo() }
if (data != null) {
userInfo = data
currentUser = data
}
}
}
4.使用 Scaffold 组件 snackbarHost 监听网络状态更新界面提示信息
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun HomePageView(navController: NavController, homeViewModel: HomeViewModel) {
LaunchedEffect(true) {
homeViewModel.getUserInfo()
}
Scaffold(containerColor = colorResource(R.color.dark), snackbarHost = {
UStatePrompt(homeViewModel, navController)
}, topBar = {
TopAppBar(colors = TopAppBarDefaults.topAppBarColors(Color.Transparent), title = {
Text(
text = homeViewModel.userInfo.orgName, color = colorResource(R.color.gray)
)
}, actions = {
IconButton(onClick = {
navController.navigate(Routes.Setting.route)
}) {
Icon(
Icons.Rounded.Settings,
contentDescription = null,
tint = colorResource(R.color.primary)
)
}
})
}) { innerPadding ->
.....
5.自定义 snackbarHost 组件 实现自定的 提示框信息
@Composable
fun UStatePrompt(viewModel: UnitMBViewModel, navController: NavController) {
val confirm = viewModel.confirmState
val httpState = viewModel.httpState
if (confirm.show) {
Dialog(onDismissRequest = {}) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(Color.White)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Icon(
modifier = Modifier.size(28.dp),
imageVector = Icons.Default.Info,
contentDescription = "",
tint = if (UConfirmType.DANGER == confirm.type) {
colorResource(R.color.danger)
} else {
colorResource(R.color.primary)
}
)
Text(
text = confirm.title, fontSize = 16.sp, fontWeight = FontWeight.Bold
)
}
Text(text = confirm.message)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(6.dp, Alignment.End),
) {
TextButton(onClick = {
viewModel.closeConfirm()
}) {
Text(
text = "取消",
fontWeight = FontWeight.Bold,
color = colorResource(R.color.dark_gray)
)
}
TextButton(onClick = {
viewModel.closeConfirm()
confirm.confirm();
}) {
Text(
text = "确定",
fontWeight = FontWeight.Bold,
color = if (UConfirmType.DANGER == confirm.type) {
colorResource(R.color.danger)
} else {
colorResource(R.color.primary)
}
)
}
}
}
}
} else if (httpState.show) {
if (httpState.isLoading) {
Dialog(onDismissRequest = {}) {
Box(
modifier = Modifier
.size(120.dp)
.clip(RoundedCornerShape(8.dp))
.background(Color.White), contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(48.dp),
color = MaterialTheme.colorScheme.primary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
}
}
} else if (!httpState.success && httpState.message != null) {
Dialog(onDismissRequest = {}) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(Color.White)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Icon(
modifier = Modifier.size(28.dp),
imageVector = Icons.Default.Error,
contentDescription = "",
tint = colorResource(R.color.danger)
)
Text(
text = "错误", fontSize = 16.sp, fontWeight = FontWeight.Bold
)
}
Text(text = httpState.message)
TextButton(modifier = Modifier.align(Alignment.End), onClick = {
viewModel.closeHttpState()
if (httpState.code == HttpError.Unauthorized.code) {
viewModel.exitLogin(navController)
}
}) {
Text(
text = "确定",
fontWeight = FontWeight.Bold,
color = colorResource(R.color.danger)
)
}
}
}
} else if (httpState.success && httpState.message != null) {
Toast.makeText(LocalContext.current, httpState.message, Toast.LENGTH_SHORT).show()
}
}
}