性能优化

This commit is contained in:
BBIT-Kai
2026-05-27 15:10:34 +08:00
parent 2017535f0f
commit 22eeac6c62
26 changed files with 204 additions and 157 deletions
+1
View File
@@ -16,3 +16,4 @@ local.properties
.idea/
app/release/
app/debug/
.kotlin/
@@ -210,8 +210,7 @@ fun MyTable(
TableContent(
verticalPadding = verticalPadding,
modifier = M
.fillMaxWidth()
.animateItem(),
.fillMaxWidth(),
backgroundDeepColor = itIndex % 2 == 0,
list = items[itIndex].map { it.toString() }.zip(ratio.map { it.toInt() }),
)
@@ -274,11 +273,12 @@ fun <T : Any> MyRefreshTableForCard(
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(count = info.itemCount, key = info.itemKey { key(it) }) { index ->
val lineItem = info[index]!!
Box(modifier = M.animateItem()) {
info[index]?.let { lineItem ->
Box {
item(lineItem, index)
}
}
}
item {
when (pagerSate) {
is LoadState.NotLoading -> if (pagerSate.endOfPaginationReached)
@@ -318,7 +318,6 @@ fun <T : Any> MyAnyTable(
val lineItem = info[index]
Card(
modifier = M
.animateItem()
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
@@ -422,7 +421,6 @@ fun <T : Any> MyTable2(
Card(
modifier = MD
.animateItem()
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
@@ -549,10 +547,9 @@ fun <T : Any> MyRefreshTable(
LazyColumn(state = listState) {
items(count = info.itemCount, key = info.itemKey { key(it) }) { index ->
// // 每行
val lineItem = info[index]!!
val lineItem = info[index] ?: return@items
Card(
modifier = MD
.animateItem()
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
@@ -11,19 +11,18 @@ abstract class BasePagingSource<T : Any, R : Any>(
val apiService = RetrofitClient.apiInterface()
abstract suspend fun fetchData(pageInfoJsonStr: String, requestData: R): List<T> // 返回 T 类型的列表
abstract suspend fun fetchData(pageInfoJsonStr: String, requestData: R): List<T>
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
return try {
val page = params.key ?: 1
val pageSize = params.loadSize
val response = fetchData(PageInfo(page, pageSize).toJson(), requestData)
// 计算上一页和下一页的 key
val prevKey = if (page > 1) page - 1 else null
val nextKey = if (response.size < pageSize) null else page + 1
LoadResult.Page(
data = response, // 直接返回数据列表
data = response,
prevKey = prevKey,
nextKey = nextKey
)
@@ -32,10 +31,10 @@ abstract class BasePagingSource<T : Any, R : Any>(
}
}
/**
* 这里可以根据当前的分页位置来返回刷新key
*/
override fun getRefreshKey(state: PagingState<Int, T>): Int {
return 1 // 默认返回1,具体实现可根据需求调整
override fun getRefreshKey(state: PagingState<Int, T>): Int? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
}
@@ -246,10 +246,8 @@ fun MainFuncFrame(
.fillMaxSize()
.background(Color.White)
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(R.drawable.bg)
.build(),
Image(
painter = painterResource(R.drawable.bg),
contentDescription = "Background",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
@@ -568,7 +566,6 @@ fun VerticalTabs(
tabs.forEachIndexed() { index, tab ->
Row(
modifier = M
.animateItem()
.clickable {
onTabSelected(index)
}
@@ -37,7 +37,9 @@ class TopInfoViewModel : BaseViewModel() {
refreshBatteryInfo()
refreshTitle()
//循环更新TopBar UI
pollingTask.startPollingTaskOnIOThread(RxTag.BAR_UI, 5) {
val dateFormat = SimpleDateFormat("yyyy/MM/dd \n")
val timeFormat = SimpleDateFormat("HH:mm")
pollingTask.startPollingTaskOnIOThread(RxTag.BAR_UI, 60) {
val calendar = Calendar.getInstance()
val lunarCalender: LunarCalendar = LunarCalendar.obtainCalendar(
calendar[Calendar.YEAR],
@@ -45,11 +47,11 @@ class TopInfoViewModel : BaseViewModel() {
calendar[Calendar.DAY_OF_MONTH]
)
_date.update {
SimpleDateFormat("yyyy/MM/dd \n").format(Date()) +
dateFormat.format(Date()) +
(lunarCalender.getLunarMonth() + "" + lunarCalender.getLunarDay())
}
_time.update {
SimpleDateFormat("HH:mm").format(Date())
timeFormat.format(Date())
}
}
}
@@ -107,7 +107,7 @@ fun AddEditUserDialog(info: AddEditUserDialogData) {
LazyColumn(modifier = M.fillMaxSize()) {
item {
Row(
modifier = M.fillMaxWidth().animateItem(),
modifier = M.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
Column(modifier = M.weight(1f)) {
@@ -99,7 +99,7 @@ fun AddWeightDialog(info: AddWeightDialogData) {
LazyColumn {
item {
Column(
modifier = M.fillMaxSize().animateItem(),
modifier = M.fillMaxSize(),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
@@ -203,7 +203,7 @@ fun Messages(
) {
items(chatRecords) {
Message(
modifier = M.animateItem(),
modifier = M,
msg = it,
isEndMessageByAuthor = true,
isNewMessageByAuthor = true
@@ -133,7 +133,7 @@ fun PriceDialog(info: PriceDialogData) {
var priceT by rememberSaveable { mutableStateOf(item.price.toString()) }
val moneySum = ((priceT.toDoubleOrNull() ?: 0.0) * item.jweightSum)
PriceTableContent(
M.animateItem(),
M,
index % 2 == 0,
item.sgTypeName,
item.weightCount.toString(),
@@ -113,7 +113,7 @@ fun WaterCutRecordDialog(info: WaterCutRecordDialogData) {
LazyColumn {
items(count = items.size) { it ->
TableContent(
modifier = M.fillMaxWidth().animateItem(),
modifier = M.fillMaxWidth(),
backgroundDeepColor = it % 2 == 0,
list = items[it].map { it.toString() }.zip(ratio.map { it.toInt() }),
verticalPadding = 15.dp,
@@ -94,7 +94,7 @@ fun DryCocoonFilterDialog(
}
}
items(tagIds.toList()) {
PackageLossInfo(M.animateItem(), it)
PackageLossInfo(M, it)
}
}
VerticalDivider(modifier = M.padding(horizontal = 5.dp))
@@ -123,7 +123,7 @@ fun DryCocoonFilterDialog(
HorizontalDivider(modifier = M.padding(vertical = 5.dp))
}
items(forceItems.toList()) {
PackageLossInfo(M.animateItem(), it)
PackageLossInfo(M, it)
}
}
}
@@ -138,7 +138,7 @@ fun DryCocoonLossDialog(
}
}
items(tagIds, key = { it }) { tag ->
PackageLossInfo(M.animateItem(),tag)
PackageLossInfo(M,tag)
}
}
}
@@ -120,7 +120,7 @@ fun DryCocoonLossDialogInOut(
}
}
items(tagIds, key = { it }) { tag ->
PackageLossInfo(M.animateItem(), tag)
PackageLossInfo(M, tag)
}
}
}
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.DatePickerDefaults
@@ -32,9 +31,11 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDateRangePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -51,6 +52,7 @@ import com.bbitcn.f8.pad.base.MainFuncFrame
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.bbitcn.f8.pad.R
import com.bbitcn.f8.pad.base.DateRangePickTextFiled
@@ -75,6 +77,7 @@ import com.bbitcn.f8.pad.ui.screen.view.drawer.DrawerViewModel
import com.bbitcn.f8.pad.ui.screen.view.drawer.StateChartsOnclick
import com.bbitcn.f8.pad.ui.theme.MyColors
import com.bbitcn.f8.pad.utils.TimeUtils
import kotlinx.coroutines.delay
import java.util.Date
/**
@@ -127,6 +130,10 @@ fun PurchaseScreen(
)
info.refresh()
}
LaunchedEffect(queryInput) {
delay(350)
updateParams()
}
MainFuncFrame {
MyCard {
Column(modifier = M.padding(15.dp)) {
@@ -170,8 +177,14 @@ fun PurchaseScreen(
Spacer(modifier = M.weight(1f))
}
DateRangePickTextFiled(dateRange = queryDateRange) {
purchaseViewModel.showDateRangeSelectDialog{
updateParams()
purchaseViewModel.showDateRangeSelectDialog { range ->
purchaseViewModel.updateParams(
range.first,
range.second,
queryInput,
queryType
)
info.refresh()
}
}
QueryTextField(
@@ -181,7 +194,6 @@ fun PurchaseScreen(
text = queryInput
) {
queryInput = it
updateParams()
}
}
if (!isLandscape()) {
@@ -216,7 +228,7 @@ fun PurchaseScreen(
}
}
}
InfoList(purchaseViewModel) {
InfoList(purchaseViewModel, info) {
drawerViewModel.openPurchaseDetailDrawer(
info = it,
StateChartsOnclick(pricing = {
@@ -311,6 +323,7 @@ fun DateRangePickerModal(
@Composable
fun InfoList(
purchaseViewModel: PurchaseViewModel,
info: LazyPagingItems<PurchaseDataResponse.Data>,
onClick: (info: PurchaseDataResponse.Data) -> Unit
) {
// 获取当前设备的屏幕配置,比如方向
@@ -322,10 +335,9 @@ fun InfoList(
} else {
4 // 横屏显示四列
}
val info = purchaseViewModel.infoPager.collectAsLazyPagingItems()
val myPager = purchaseViewModel.infoMyPager
val isRefreshing by myPager.listIsRefreshing.collectAsState()
var selectIndex by rememberSaveable { mutableStateOf(-1) }
var selectId by rememberSaveable { mutableStateOf("") }
MyRefreshTableForCard(
modifier = M
.fillMaxWidth()
@@ -338,9 +350,9 @@ fun InfoList(
},
columns = columns,
item = { data, index ->
InfoItem(isSelect = index == selectIndex, data = data) {
InfoItem(isSelect = data.czSysid == selectId, data = data) {
onClick(data)
selectIndex = index
selectId = data.czSysid
}
}
)
@@ -352,6 +364,10 @@ fun InfoItem(
data: PurchaseDataResponse.Data,
onClick: () -> Unit
) {
val visibleItems = remember(data.chengZhongItemSumList) {
data.chengZhongItemSumList.take(2)
}
val hiddenCount = data.chengZhongItemSumList.size - visibleItems.size
MyCard(
radius = 7.dp,
elevation = 0.dp,
@@ -401,11 +417,10 @@ fun InfoItem(
Pair("净重", 1), Pair("单价", 1)
), isSelect
)
LazyColumn(modifier = M.height(60.dp)) {
items(count = data.chengZhongItemSumList.size) { index ->
val item = data.chengZhongItemSumList[index]
Column(modifier = M.height(60.dp)) {
visibleItems.forEach { item ->
TableContent(
M.fillMaxWidth().animateItem(), isSelect,
M.fillMaxWidth(), isSelect,
listOf(
Pair(item.sgTypeName, 1),
Pair(item.mweightSum.toString(), 1),
@@ -416,6 +431,16 @@ fun InfoItem(
), 3.5.dp
)
}
if (hiddenCount > 0) {
Text(
text = "+$hiddenCount",
modifier = M
.fillMaxWidth()
.padding(top = 2.dp, end = 8.dp),
color = if (isSelect) MyColors.BlueGreen else MyColors.Gray,
fontSize = MaterialTheme.typography.labelSmall.fontSize
)
}
}
StateList(
data.ispPicing, data.isKouPiing,
@@ -78,7 +78,7 @@ fun StatisticsScreen(
.fillMaxSize()
.padding(10.dp)
) {
StatisticsLeft(modifier = M.weight(2f), statisticsViewModel, curType) {
StatisticsLeft(modifier = M.weight(2f), statisticsViewModel, curType, pager) {
curType = it
statisticsViewModel.updateParamsCocoonType(it.sgtypesysid)
pager.refresh()
@@ -114,11 +114,11 @@ fun StatisticsLeft(
modifier: Modifier,
statisticsViewModel: StatisticsViewModel,
selectedTabIndex: StatisticsResponse.Data,
pager: LazyPagingItems<StatisticsListResponse.Data>,
onFilterSelectedTabChanged: (StatisticsResponse.Data) -> Unit
) {
val daysInfo by statisticsViewModel.daysInfo.collectAsState()
val statistics by statisticsViewModel.statistics.collectAsState()
val pager = statisticsViewModel.statisticsPager.collectAsLazyPagingItems()
Column(
modifier = modifier
@@ -47,6 +47,7 @@ import com.bbitcn.f8.pad.base.MainFuncFrame
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.bbitcn.f8.pad.R
import com.bbitcn.f8.pad.base.AssistChipFilter
@@ -58,6 +59,7 @@ import com.bbitcn.f8.pad.base.MyRefreshTable
import com.bbitcn.f8.pad.base.MyTableData
import com.bbitcn.f8.pad.base.QueryTextField
import com.bbitcn.f8.pad.base.isLandscape
import com.bbitcn.f8.pad.model.net.response.UserDataResponse
import com.bbitcn.f8.pad.ui.theme.MyColors
import com.bbitcn.f8.pad.utils.MyUtil
import com.bbitcn.f8.pad.utils.TimeUtils
@@ -82,11 +84,12 @@ fun UserScreen(
userViewModel: UserViewModel = viewModel()
) {
val treeData by userViewModel.treeData.collectAsState()
val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems()
MainFuncFrame {
if (isLandscape()) {
UserScreenInLandscape(navController, userViewModel, treeData)
UserScreenInLandscape(navController, userViewModel, treeData, userData)
} else {
UserScreenInPortrait(navController, userViewModel, treeData)
UserScreenInPortrait(navController, userViewModel, treeData, userData)
}
}
}
@@ -95,7 +98,8 @@ fun UserScreen(
fun UserScreenInPortrait(
navController: NavController,
userViewModel: UserViewModel,
treeData: List<Pair<Pair<String, Int>, Any>>
treeData: List<Pair<Pair<String, Int>, Any>>,
userData: LazyPagingItems<UserDataResponse.Data>
) {
Column(
modifier = M.fillMaxSize()
@@ -105,12 +109,12 @@ fun UserScreenInPortrait(
.weight(2f)
.padding(bottom = 15.dp)
) {
CollapsibleList(userViewModel, treeData)
CollapsibleList(userViewModel, treeData, userData)
}
MyInfoCard(
modifier = M.weight(8f)
) {
UserManageList(navController, userViewModel)
UserManageList(navController, userViewModel, userData)
}
}
}
@@ -119,7 +123,8 @@ fun UserScreenInPortrait(
fun UserScreenInLandscape(
navController: NavController,
userViewModel: UserViewModel,
treeData: List<Pair<Pair<String, Int>, Any>>
treeData: List<Pair<Pair<String, Int>, Any>>,
userData: LazyPagingItems<UserDataResponse.Data>
) {
var leftWeight by rememberSaveable { mutableStateOf(2.5f) }
var rightWeight by rememberSaveable { mutableStateOf(7.5f) }
@@ -131,7 +136,7 @@ fun UserScreenInLandscape(
.weight(leftWeight) // 使用动态权重
.fillMaxHeight()
) {
CollapsibleList(userViewModel, treeData)
CollapsibleList(userViewModel, treeData, userData)
}
Box(
modifier = M
@@ -160,15 +165,18 @@ fun UserScreenInLandscape(
.weight(rightWeight) // 使用动态权重
.fillMaxHeight()
) {
UserManageList(navController, userViewModel)
UserManageList(navController, userViewModel, userData)
}
}
}
@Composable
fun CollapsibleList(userViewModel: UserViewModel, listData: List<Pair<Pair<String, Int>, Any>>) {
fun CollapsibleList(
userViewModel: UserViewModel,
listData: List<Pair<Pair<String, Int>, Any>>,
pager: LazyPagingItems<UserDataResponse.Data>
) {
val queryInput by userViewModel.areaLike.collectAsState()
val pager = userViewModel.usersInfoPager.collectAsLazyPagingItems()
Column {
Row(
modifier = M
@@ -194,6 +202,7 @@ fun CollapsibleList(userViewModel: UserViewModel, listData: List<Pair<Pair<Strin
items(listData) { item ->
CollapsibleItem(
userViewModel,
pager,
item = item,
currentLevel = 1,
needExpand = queryInput != ""
@@ -206,6 +215,7 @@ fun CollapsibleList(userViewModel: UserViewModel, listData: List<Pair<Pair<Strin
@Composable
fun CollapsibleItem(
userViewModel: UserViewModel,
pager: LazyPagingItems<UserDataResponse.Data>,
item: Pair<Pair<String, Int>, Any>,
currentLevel: Int,
xian: String = "",
@@ -225,7 +235,6 @@ fun CollapsibleItem(
expandedIndex = if (needExpand) 0 else -1
}
}
val pager = userViewModel.usersInfoPager.collectAsLazyPagingItems()
// 被选中的项
val xianTmp = if (currentLevel == 1) titleAndCount.first else xian
val xiangTmp = if (currentLevel == 2) titleAndCount.first else xiang
@@ -327,6 +336,7 @@ fun CollapsibleItem(
val subPair = subItem.key to subItem.value
CollapsibleItem(
userViewModel,
pager,
subPair as Pair<Pair<String, Int>, Any>,
currentLevel + 1,
xianTmp,
@@ -407,10 +417,13 @@ fun CollapsibleItem(
}
@Composable
fun UserManageList(navController: NavController, userViewModel: UserViewModel) {
fun UserManageList(
navController: NavController,
userViewModel: UserViewModel,
userData: LazyPagingItems<UserDataResponse.Data>
) {
var queryInput by rememberSaveable { mutableStateOf("") }
val areaFilter by userViewModel.areaFilter.collectAsState()
val userData = userViewModel.usersInfoPager.collectAsLazyPagingItems()
val onFilterLikeChanged: (String) -> Unit = {
queryInput = it
userViewModel.updateParamsLike(it)
@@ -176,7 +176,7 @@ fun AddUserBaseInfo(
LazyColumn(modifier = M.padding(10.dp)) {
item {
BigButton(
modifier = M.padding(top = 15.dp).animateItem(),
modifier = M.padding(top = 15.dp),
text = "保存",
isLight = true
) {
@@ -213,7 +213,7 @@ fun AddUSerExtendInfo(addUserViewModel: AddUserViewModel) {
val userExtendList by addUserViewModel.userExtendList.collectAsState()
LazyColumn {
item {
Column(modifier = M.padding(top = 5.dp).animateItem()) {
Column(modifier = M.padding(top = 5.dp)) {
Row(
horizontalArrangement = Arrangement.spacedBy(5.dp),
verticalAlignment = Alignment.CenterVertically
@@ -260,8 +260,7 @@ fun WeightList(weightViewModel: WeightViewModel) {
val item = detailList[it]
TableContent(
modifier = M
.fillMaxWidth()
.animateItem(),
.fillMaxWidth(),
backgroundDeepColor = it % 2 == 0,
list = listOf(
item.weighingTimes.toString() to 1,
@@ -457,7 +456,7 @@ fun WeightRight(navController: NavController, weightViewModel: WeightViewModel)
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(kindsInfo) {
AddKindsButton(M.animateItem(), curWeight, weightViewModel, it, errMsg)
AddKindsButton(M, curWeight, weightViewModel, it, errMsg)
}
}
Row(
@@ -389,7 +389,6 @@ fun ReaderSerialPort(
items(labelList) { tag ->
Text(
modifier = M
.animateItem()
.fillMaxWidth()
.padding(10.dp),
text = tag,
@@ -565,7 +564,6 @@ fun ConnectBluetooth(bluetooth: MyBlueTooth) {
println("bluetooth.device.value?.address: ${bluetooth.device.value?.address}")
Row(
modifier = M
.animateItem()
.fillMaxWidth()
.clickable { bluetooth.connect(device.address) },
) {
@@ -141,7 +141,7 @@ fun TicketForPurchase(
)
LazyColumn(modifier = M.weight(1f)) {
items(info.chengZhongItemSumList) {
SilkKindCard(M.animateItem(),it)
SilkKindCard(M,it)
}
}
Column {
@@ -1,11 +1,8 @@
package com.bbitcn.f8.pad.utils.network
import com.bbitcn.f8.pad.R
import com.bbitcn.f8.pad.model.net.response.CommonResponse
import com.bbitcn.f8.pad.ui.screen.view.Toasty
import com.bbitcn.f8.pad.utils.AudioPlayer
import com.bbitcn.f8.pad.utils.MMKVUtil
import com.bbitcn.f8.pad.utils.TTSManager
import com.bbitcn.f8.pad.utils.global.RxTag
import com.bbitcn.f8.pad.utils.log.MyLog
import com.google.gson.Gson
@@ -14,7 +11,6 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.Interceptor
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Protocol
@@ -23,7 +19,6 @@ import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
@@ -34,50 +29,43 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object RetrofitClient {
const val BASE_URL = "http://f8.api.app.bbitcn.cn/" // 生产环境
// const val BASE_URL = "http://172.20.50.11:5291/" //何 工厂
// const val BASE_URL = "http://10.0.4.30:5291/" //何
// const val BASE_URL = "http://10.0.4.50:5291/" //罗
// const val BASE_URL = "http://10.0.4.68:5291/" //孔
const val BASE_URL = "http://f8.api.app.bbitcn.cn/"
private const val ERROR_BODY_PEEK_BYTES = 64 * 1024L
private const val ENABLE_BODY_LOG = false
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
level = if (ENABLE_BODY_LOG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
val errorInterceptor = Interceptor { chain ->
// 获取原始的响应
try {
val response = chain.proceed(chain.request())
// 先读取响应体的内容,但不消耗流
val responseBody = response.body
val content = responseBody?.string() // 这里消耗了响应体,但我们要重新创建响应体
// 将响应体内容保存到新的 ResponseBody
val newResponseBody = ResponseBody.create(responseBody?.contentType(), content ?: "")
// 重新构造响应,避免影响后续的流操作
val newResponse = response.newBuilder()
.body(newResponseBody) // 替换成新的响应体
.build()
// 如果响应码不是 200 或 401,显示错误消息
if (newResponse.code !in listOf(200, 401)) {
// todo 如果断网 这里会出现 com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
// 暂定是服务器的断点导致的
val commonResponse = Gson().fromJson(content, CommonResponse::class.java)
if (response.code !in listOf(200, 401)) {
val content = response.peekBody(ERROR_BODY_PEEK_BYTES).string()
runCatching {
Gson().fromJson(content, CommonResponse::class.java)
}.onSuccess { commonResponse ->
Toasty.showTipsDialog("网络请求错误:${commonResponse.msg}")
if (commonResponse.code == 401 && MMKVUtil.get(RxTag.REFRESH_TOKEN).isEmpty()) {
Toasty.loginExpired()
}
}.onFailure {
Toasty.showTipsDialog(response.message)
}
// 返回新的响应
return@Interceptor newResponse
}
return@Interceptor response
} catch (e: Exception) {
e.printStackTrace()
MyLog.networkError("请求网络时发生异常:${e.message}")
MyLog.networkError("错误类型:${e.javaClass.simpleName},错误信息:${e.message}")
val responseBody =
ResponseBody.create("application/json".toMediaTypeOrNull(), "{}") // 空的 JSON
ResponseBody.create("application/json".toMediaTypeOrNull(), "{}")
val fakeResponse: Response =
if (e is UnknownHostException || e is SocketTimeoutException || e is ConnectException) {
Response.Builder()
@@ -100,13 +88,18 @@ object RetrofitClient {
}
}
private val retrofit: Retrofit by lazy { createRetrofit() }
private val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) }
@Provides
@Singleton
fun myRetrofit(): Retrofit {
fun myRetrofit(): Retrofit = retrofit
private fun createRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(// 设置 OkHttpClient
.client(
OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
@@ -114,10 +107,10 @@ object RetrofitClient {
.build()
chain.proceed(request)
}
.connectTimeout(20, TimeUnit.SECONDS) // 设置连接超时时间
.readTimeout(20, TimeUnit.SECONDS) // 设置读取超时时间
.writeTimeout(20, TimeUnit.SECONDS) // 设置写入超时时间
.authenticator(TokenAuthenticator()) // 添加401拦截器
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.authenticator(TokenAuthenticator())
.addInterceptor(loggingInterceptor)
.addInterceptor(errorInterceptor)
.build()
@@ -127,7 +120,5 @@ object RetrofitClient {
@Provides
@Singleton
fun apiInterface(): ApiService =
RetrofitClient_MyRetrofitFactory.myRetrofit().create(ApiService::class.java)
fun apiInterface(): ApiService = apiService
}
@@ -16,10 +16,14 @@ import javax.inject.Singleton
object RetrofitClientAI {
const val AI_BASE_URL = "http://171.212.101.199:13010/"
private val retrofit: Retrofit by lazy { createRetrofit() }
private val apiService: AIApiService by lazy { retrofit.create(AIApiService::class.java) }
@Provides
@Singleton
fun myAIRetrofit(): Retrofit {
fun myAIRetrofit(): Retrofit = retrofit
private fun createRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(AI_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
@@ -41,6 +45,6 @@ object RetrofitClientAI {
@Provides
@Singleton
fun aiApiInterface(): AIApiService = myAIRetrofit().create(AIApiService::class.java)
fun aiApiInterface(): AIApiService = apiService
}
@@ -16,10 +16,14 @@ import javax.inject.Singleton
object RetrofitClientFace {
const val Face_BASE_URL = "https://aip.baidubce.com/"
private val retrofit: Retrofit by lazy { createRetrofit() }
private val apiService: AIApiService by lazy { retrofit.create(AIApiService::class.java) }
@Provides
@Singleton
fun myAIRetrofit(): Retrofit {
fun myAIRetrofit(): Retrofit = retrofit
private fun createRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(Face_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
@@ -40,6 +44,6 @@ object RetrofitClientFace {
@Provides
@Singleton
fun faceApiInterface(): AIApiService = myAIRetrofit().create(AIApiService::class.java)
fun faceApiInterface(): AIApiService = apiService
}
@@ -16,10 +16,14 @@ import javax.inject.Singleton
object RetrofitClientIOT {
const val AI_BASE_URL = "https://iot.api.bbitcn.net/"
private val retrofit: Retrofit by lazy { createRetrofit() }
private val apiService: ApiServiceIOT by lazy { retrofit.create(ApiServiceIOT::class.java) }
@Provides
@Singleton
fun myAIRetrofit(): Retrofit {
fun myAIRetrofit(): Retrofit = retrofit
private fun createRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(AI_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
@@ -41,6 +45,6 @@ object RetrofitClientIOT {
@Provides
@Singleton
fun apiInterface(): ApiServiceIOT = myAIRetrofit().create(ApiServiceIOT::class.java)
fun apiInterface(): ApiServiceIOT = apiService
}
@@ -13,59 +13,72 @@ import okhttp3.Response
import okhttp3.Route
/**
* 401 拦截器
* 401 token refresh interceptor.
*/
class TokenAuthenticator() : Authenticator {
class TokenAuthenticator : Authenticator {
private val lock = Any() // 用于同步刷新 token 的操作
private var isRefreshing = false // 标志:是否正在刷新 token
private val lock = Any()
override fun authenticate(route: Route?, response: Response): Request? {
MyLog.network("TokenAuthenticator: authenticate")
if (response.code == 401) {
MyLog.network("401TokenAuthenticator: authenticate")
if (MMKVUtil.get(RxTag.REFRESH_TOKEN).isEmpty()) {
Toasty.loginExpired()
}
synchronized(lock) {
// 如果正在刷新 token,直接返回 null,等待刷新完成
if (isRefreshing)
if (response.code != 401 || responseCount(response) >= 2) {
return null
isRefreshing = true
}
if (response.request.url.encodedPath.contains("RefreshToken", ignoreCase = true)) {
Toasty.loginExpired()
return null
}
synchronized(lock) {
val refreshToken = MMKVUtil.get(RxTag.REFRESH_TOKEN, "")
if (refreshToken.isEmpty()) {
Toasty.loginExpired()
return null
}
val currentToken = MMKVUtil.get(RxTag.ACCESS_TOKEN, "")
val requestToken = response.request.header("Authorization")?.removePrefix("Bearer ")
if (currentToken.isNotEmpty() && requestToken != currentToken) {
return response.request.withToken(currentToken)
}
val newToken = runBlocking {
val accessToken = MMKVUtil.get(RxTag.ACCESS_TOKEN, "")
val refreshToken = MMKVUtil.get(RxTag.REFRESH_TOKEN, "")
MyLog.network("刷新Token: $accessToken, $refreshToken")
MyLog.network("刷新Token: $currentToken, $refreshToken")
val result = RetrofitClient.apiInterface().refreshToken(
RefreshTokenRequest(
hardwareid = Global.getDeviceId(),
refToken = refreshToken,
token = accessToken
token = currentToken
)
)
if (result.code == 1) {
MMKVUtil.put(RxTag.ACCESS_TOKEN, result.data.accessToken)
result.data.accessToken
} else {
// 刷新 token 失败,跳转到登录页面
Toasty.loginExpired()
MyLog.networkError("刷新Token失败,跳转到登录页面: ${result.msg}")
null
}
}
synchronized(lock) {
isRefreshing = false // 刷新完成,允许其他请求进行刷新
return newToken?.let { response.request.withToken(it) }
}
}
return newToken?.let {
response.request.newBuilder()
.header("Authorization", "Bearer $it")
private fun Request.withToken(token: String): Request {
return newBuilder()
.header("Authorization", "Bearer $token")
.build()
}
private fun responseCount(response: Response): Int {
var count = 1
var prior = response.priorResponse
while (prior != null) {
count++
prior = prior.priorResponse
}
return null
return count
}
}
@@ -19,7 +19,7 @@ class MyPager<T : Any, R : Any>(
initialRequestData: R, // 初始的请求数据
private val initialLoadSize: Int = 12,
private val pageSize: Int = 12,
private val enablePlaceholders: Boolean = true
private val enablePlaceholders: Boolean = false
) {
// 内部的 MutableStateFlow,用来保存请求数据