问题描述
我使用androidx分页3实现,遇到了一个奇怪的问题。
在某些情况下,我的“ okhttp3 retrofit2 https GET请求”被意外取消了。
我的简单问题是:OkHttpClient何时意外取消请求? (我没有从任何地方发送任何取消请求。)
- 第二次发送同一请求时是否存在(如果我执行“刷新”请求,则最有可能发生。由于服务器中的预期更改,IE第二次将同一请求发送到服务器,但没有更改完成,所以结果是相同的。)
- 对服务器的并发请求是否有限制。
- 远程服务器可以取消吗?
- 如果请求是在请求响应内死掉的线程中完成的,则可以取消该请求,如果是的话,我如何防止它发生(这很奇怪...,该请求在70毫秒内被取消,并且是从kotlin完成的)暂停通话...)?
我的API客户端工厂:
object APIFamappClientFactory{
private var apiFamappInterfaceService: APIFamappInterfaceService ?= null
fun makeAPIFamappInterfaceService(): APIFamappInterfaceService{
if(apiFamappInterfaceService == null) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient
.Builder()
.addInterceptor(interceptor)
.readTimeout(30,TimeUnit.SECONDS)
.build()
apiFamappInterfaceService = Builder()
.baseUrl("https://www.famapp.no")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(APIFamappInterfaceService::class.java)
}
return apiFamappInterfaceService!!
}
}
其中一个被取消的查询。
interface APIFamappInterfaceService{
//---
@GET("/fuelpump/liststations_fp3_v6.php")
suspend fun getSimpleStationsGlobalNearest(
@Query("userid") userid: String,@Query("passfrase") passfrase: String,@Query("latitude") latitude: String,@Query("longitude") longitude: String,@Query("limit") limit: String,@Query("offset") offset: String,@Query("lastversion") lastversion: String,@Query("killed") killed: String,@Query("range") range:String
): List<APISimpleStation>
//---
}
在浏览器中粘贴请求字符串,我没有发现错误,结果还可以,所以我不怀疑服务器端...
片段中的某个位置,用于填充recyclerview的流程实现
viewModelJob = lifecycleScope.launch {
viewModel.getStationsFlow().collectLatest {stationWithKindPricePagingData ->
adapter.submitData(stationWithKindPricePagingData)
}
}
视图模型中的流程实现(虽然简单)
class StationListViewModel internal constructor(
private val stationListRepository: StationListRepository
/*unsignificants omitted*/
) : ViewModel()
{
//---
fun getStationsFlow() = stationListRepository.getStationsFlow()
//---
}
在存储库中,我将Pager实现与RemoteMediator一起使用
class StationListRepository private constructor(
private val appDatabase: AppDatabase
)
{
//----
fun getStationsFlow(): Flow<PagingData<StationWithKindPrice>>
{
val pagingSourceFactory = {
stationListPagingSource = appDatabase
.stationDao()
.getLivePagingSourceStationsWithKindPriceUser("user")
stationListPagingSource
}
return Pager(
config = PagingConfig(pageSize = 20),remoteMediator = StationListRemoteMediator2(appDatabase),pagingSourceFactory = pagingSourceFactory
).flow
}
}
RemoteMediator相当复杂,因此我已将其全部驱除...
private const val STATION_LIST_STARTING_PAGE_INDEX = 1
@OptIn(ExperimentalPagingApi::class)
class StationListRemoteMediator2(
private val appDatabase: AppDatabase
): RemoteMediator<Int,StationWithKindPrice>(){
private val TAG by lazy { this::class.java.simpleName }
override suspend fun load(
loadType: LoadType,state: PagingState<Int,StationWithKindPrice>
): MediatorResult {
Log.i(TAG,"load: ( loadType = $loadType )")
var page = when(loadType){
LoadType.REFRESH -> {
Log.i(TAG,"load: LoadType.REFRESH returning null (initial load)")
null
}
LoadType.PREPEND -> {
Log.i(TAG,"load: LoadType.PREPEND state.lastItemOrNull()?.stationId = " +
"${state.lastItemOrNull()?.stationId}")
Log.i(TAG,"load: LoadType.PREPEND returning endOfPaginationReached = true,returning")
return MediatorResult.Success(endOfPaginationReached = true)
}
LoadType.APPEND -> {
Log.i(TAG,"load: LoadType.APPEND preparing lastItem")
val lastItem = state.lastItemOrNull()?.let {
Log.i(TAG,"load: LoadType.APPEND state.lastItem.stationId = ${it.stationId}")
return@let it
} ?: let {
Log.i(TAG,"load: LoadType.APPEND state.lastItem = null,no data to append,returning")
return MediatorResult.Success(endOfPaginationReached = true)
}
Log.i(TAG,"load: LoadType.APPEND preparing remoteKey")
val remoteKey = appDatabase.withTransaction {
Log.i(
TAG,"load: LoadType.APPEND querying remoteKeyFromStationId " +
"lastItem.stationID = ${lastItem.stationId}"
)
val key = appDatabase.stationListRemoteKeyDao()
.remoteKeyFromStationId(lastItem.stationId)
Log.i(TAG,"load: LoadType.APPEND returning key = $key")
key
}?.let {
Log.i(TAG,"load: LoadType.APPEND remoteKeyFromStationId found = $it")
return@let it
} ?: let {
Log.w(TAG,"load: LoadType.APPEND remoteKeyFromStationID found null,returning")
return MediatorResult.Success(endOfPaginationReached = true)
}
Log.i(
TAG,"load: LoadType.APPEND returning remoteKey.nextKey = ${remoteKey.nextKey}"
)
remoteKey.nextKey ?: return MediatorResult.Success(endOfPaginationReached = true)
}
}
Log.i(TAG,"load: Begin try api")
try {
if(page==null) page=1
val apiService = APIFamappClientFactory.makeAPIFamappInterfaceService()
var endOfPaginationReached = false
val deviceLocation =
appDatabase.deviceLocationDao().getNullableDeviceLocation()?.let {
Log.i(TAG,"load: getNullableDeviceLocation = $it")
return@let it
} ?:let{
Log.w(TAG,"load: getNullableDeviceLocation == null",)
return MediatorResult.Error(Throwable("No location data!!"))
}
Log.i(TAG,"load: deviceLocation = $deviceLocation")
val user = appDatabase.userDao().getUser()
?:return MediatorResult.Error(Throwable("No user registered yet !!"))
Log.i(TAG,"load: user = $user")
val userSelection = appDatabase.userSelectionDao().getNullableUserSelection()
?:return MediatorResult.Error(Throwable("No user selections !!"))
Log.i(TAG,"load: userSelection = $userSelection")
var range:String? = if(page <= 1){
Log.i(TAG,"load: resets range to 1.0")
CoroutineScope(Dispatchers.IO).launch {
appDatabase.userSelectionDao().updateCurrentRange("1.0")
}
"1.0"
} else userSelection.currentRange;
Log.i(TAG,"load: range = $range")
val countryCode: String
countryCode = if(userSelection.selectionAutoCountry)
{
deviceLocation.countrycode
?:user.tmsNWCountryISO?.toUpperCase(Locale.ROOT)
?:user.tmsSIMCountryISO?.toUpperCase(Locale.ROOT)
?:userSelection.selectionCountry?:"--"
}
else
{
userSelection.selectionCountry
?:user.tmsNWCountryISO?.toUpperCase(Locale.ROOT)
?:user.tmsSIMCountryISO?.toUpperCase(Locale.ROOT)
?:deviceLocation.countrycode?:"--"
}
Log.i(TAG,"load: countryCode = $countryCode")
Log.i(TAG,"load: userSelection.selectionArea = ${userSelection.selectionArea}")
Log.i(TAG,"load: userSelection.selectionSorting = ${userSelection.selectionSorting}")
var req: List<APISimpleStation>?=null
if((userSelection.selectionArea == "Country" )
&&(userSelection.selectionSorting == "Nearest"))
{
do {
if (userSelection.selectionAutoCountry) {
Log.i(TAG,"load: country,nearest,selectionAutoCountry = true,range = $range")
req = apiService.getSimpleStationsCountryNearest(
userid = user.userId,passfrase = user.passfrase,country = countryCode,latitude = deviceLocation.latitude.toString()
.format(Locale.ROOT,"%s"),longitude = deviceLocation.longitude.toString()
.format(Locale.ROOT,limit = state.config.pageSize.toString(),offset = ((page - 1) * state.config.pageSize).toString(),lastversion = user.currentVersion,killed = if (userSelection.showHidden) "1" else "0",range = range ?: "1.0" //may be changed
)
} else {
Log.i(TAG,selectionAutoCountry = false,range = $range")
val countryData = appDatabase.countryDao().getCountryByCode(countryCode)
Log.i(TAG,"load: autoCountry not auto set = ${countryData?.isoCC2}")
req = apiService.getSimpleStationsCountryNearest(
userid = user.userId,latitude = (countryData?.latitude_center ?: 0.0F).toString()
.format(Locale.ROOT,longitude = (countryData?.longitude_center ?: 0.0F).toString()
.format(Locale.ROOT,range = range ?: "1.0" //must be changed
)
}
Log.i(TAG,"load: req.size = ${req.size}")
if(req.isEmpty()) range = increaseRange(range)
else break
}while (range != null)
}else if((userSelection.selectionArea == "Global")
&&(userSelection.selectionSorting == "Nearest"))
{
do{
Log.i(TAG,"load: global,range = $range")
req = apiService.getSimpleStationsGlobalNearest(
userid = user.userId,latitude = deviceLocation.latitude.toString().format(Locale.ROOT,longitude = deviceLocation.longitude.toString().format(Locale.ROOT,offset = ((page-1)*state.config.pageSize).toString(),killed = if(userSelection.showHidden) "1" else "0",range = range?:"1.0"
)
Log.i(TAG,"load: req.size = ${req.size}")
if(req.isEmpty()) range = increaseRange(range)
else break
}while (range != null)
}else if((userSelection.selectionArea == "Country")
&&(userSelection.selectionSorting == "Latest"))
{
Log.i(TAG,latest")
req = apiService.getSimpleStationsCountryLatest(
userid = user.userId,killed = if(userSelection.showHidden) "1" else "0"
)
}else if((userSelection.selectionArea == "Global")
&&(userSelection.selectionSorting == "Latest"))
{
Log.i(TAG,latest")
req = apiService.getSimpleStationsGlobalLatest(
userid = user.userId,killed = if(userSelection.showHidden) "1" else "0"
)
}
//TODO: Add rest
endOfPaginationReached = req?.isEmpty()
?:let {
Log.i(TAG,"load: req is empty,returning MediatorResult.Error")
return MediatorResult.Error(Throwable("No data could be read from backend...")) }
if(loadType == LoadType.REFRESH)
{
Log.i(TAG,"load: start clear appDatabase.withTransaction")
appDatabase.withTransaction {
appDatabase.stationListRemoteKeyDao().clearStationListRemoteKeys()
appDatabase.stationDao().nukeTable() //Nuke station table
}
}
Log.i(TAG,"load: start appDatabase.withTransaction")
appDatabase.withTransaction {
val prevKey = if(page == STATION_LIST_STARTING_PAGE_INDEX) null else page -1
val nextKey = if(endOfPaginationReached) null else page + 1
val keys = req.map {
StationListRemoteKey(
stationId = it.idsite,prevKey = prevKey,nextKey = nextKey)
}
val stations = req.map{
Log.i(
TAG,"load: station map ${it.idsite}: ${it.stationname}: ${it.latitude},${it.longitude} -> ${deviceLocation.latitude},${deviceLocation.longitude}"
)
Station(
stationId = it.idsite,stationName = it.stationname,longitude = it.longitude.toDoubleOrNull(),latitude = it.latitude.toDoubleOrNull(),airDistance = (locationDistance(
it.latitude.toDoubleOrNull(),it.longitude.toDoubleOrNull(),deviceLocation.latitude,deviceLocation.longitude
)?:0.0)/1000.0,enterpriseId = it.enterpriseid,googlePlaceId = it.googleplaceid,stationAddress = it.stationaddress,stationPlace = it.stationplace,stationZip = it.stationzip,stationCountryCode = it.countrycode,killed = it.killed?.equals("1")?:false,dateCreated =it.datecreated,stationCountry = it.stationcountry
)
}
appDatabase.stationListRemoteKeyDao().insertAll(keys)
appDatabase.stationDao().insertAll(stations)
}
Log.i(TAG,"load: appDatabase.withTransaction end returing endofPaginationReached = $endOfPaginationReached")
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
}catch (exception: IOException)
{
return MediatorResult.Error(exception)
}
catch (exception: HttpException)
{
return MediatorResult.Error(exception)
}
}
fun increaseRange(inRange: String?): String? {
var fRange:Float = inRange?.toFloatOrNull()?:1.0F
fRange += 1.0F
Log.i(TAG,"increaseRange: fRange = $fRange")
val rRange = if(fRange>180.0F) null
else fRange.toString().format(Locale.ROOT,"%0.1f")
CoroutineScope(Dispatchers.IO).launch {
appDatabase.userSelectionDao().updateCurrentRange(rRange)
}
return rRange
}
fun locationDistance(
fromLatitude: Double?,fromLongitude: Double?,toLatitude: Double?,toLongitude : Double?
):Double?{
fromLatitude?:return null
fromLongitude?:return null
toLatitude?:return null
toLongitude?:return null
val fromLocation = Location("FromLocation")
val toLocation = Location("ToLocation")
fromLocation.latitude = fromLatitude
fromLocation.longitude = fromLongitude
toLocation.latitude = toLatitude
toLocation.longitude = toLongitude
return fromLocation.distanceTo(toLocation).toDouble()
}
}
如果您深入其中,我将看不到任何取消...
2020-08-16 10:31:35.918 30686-30686/no.rogo.emptyfuel I/MainActivity: createLocationCallback: onLocationResult locationCallback last location registered: Location[fused 61.890727,6.676107 hAcc=5 et=+4d1h19m58s848ms alt=0.0 vel=0.0 bear=90.0 vAcc=1 sAcc=1 bAcc=30 {Bundle[mParcelledData.dataSize=52]}]
2020-08-16 10:31:35.918 30686-30686/no.rogo.emptyfuel I/StationListRepository: upd: updateAirDistance2():
2020-08-16 10:31:35.923 30686-31093/no.rogo.emptyfuel I/StationListRepository: countrycode = NO (Norway)
2020-08-16 10:31:35.924 30686-31093/no.rogo.emptyfuel I/StationListRepository: deviceLocation altered !
2020-08-16 10:31:35.928 30686-31093/no.rogo.emptyfuel I/StationListRepository: oldcountrycode = NO,countrycode = NO
2020-08-16 10:31:35.929 30686-31093/no.rogo.emptyfuel I/StationListRepository: upd: Waiting for staions
2020-08-16 10:31:35.930 30686-30686/no.rogo.emptyfuel I/StationListFragment: location accuracy 5.0
2020-08-16 10:31:35.931 30686-30686/no.rogo.emptyfuel I/StationListFragment: viewModel.liveDeviceLocation.observing 61.8907267 6.6761067
2020-08-16 10:31:35.938 30686-31093/no.rogo.emptyfuel I/StationListRepository: upd: Stations read: 80
2020-08-16 10:31:35.938 30686-31093/no.rogo.emptyfuel I/StationListRepository: updateAirDistance2: location = Location[fused 61.890727,6.676107 hAcc=5 et=+4d1h19m58s848ms alt=0.0 vel=0.0 bear=90.0 vAcc=1 sAcc=1 bAcc=30 {Bundle[mParcelledData.dataSize=52]}]
2020-08-16 10:31:35.948 30686-30686/no.rogo.emptyfuel I/BA isGone: Visible
2020-08-16 10:31:35.948 30686-30686/no.rogo.emptyfuel I/BA isGone: Gone
2020-08-16 10:31:37.590 30686-30686/no.rogo.emptyfuel I/StationListFragment: swipeRefresh !
2020-08-16 10:31:37.595 30686-30686/no.rogo.emptyfuel I/StationListFragment: subscribeUi: viewModelJob collecting flow
2020-08-16 10:31:37.598 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: ( loadType = REFRESH )
2020-08-16 10:31:37.598 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: LoadType.REFRESH returning null (initial load)
2020-08-16 10:31:37.598 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: Begin try api
2020-08-16 10:31:37.598 30686-30686/no.rogo.emptyfuel I/APIFamappClientFactory: API: returning apiFamappInterfaceService = retrofit2.Retrofit$1@1679425
2020-08-16 10:31:37.599 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: .addLoadStateListener combinedLoadStates = CombinedLoadStates(source=LoadStates(refresh=NotLoading(endOfPaginationReached=false),prepend=NotLoading(endOfPaginationReached=true),append=NotLoading(endOfPaginationReached=false)),mediator=LoadStates(refresh=Loading(endOfPaginationReached=false),append=NotLoading(endOfPaginationReached=false)))
2020-08-16 10:31:37.599 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: combinedLoadStates.refresh = Loading(endOfPaginationReached=false)
2020-08-16 10:31:37.643 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: getNullableDeviceLocation = DeviceLocation(userId=user,location_valid=true,timestamp=2020-08-16 08:31:35,latitude=61.8907267,longitude=6.6761067,locationAccuracy=5.0,hasLocationAccuracy=true,altitude=0.0,altitudeAccuracy=0.5,hasAltitude=true,hasAltitudeAccuracy=true,bearing=90.0,bearingAccuracy=30.0,hasBearing=true,hasBearingAccuracy=true,speed=0.0,speedAccuracy=0.5,hasSpeed=true,hasSpeedAccuracy=true,isFromMockProvider=false,provider=fused,countrycode=NO,countryname=Norway,hasCountry=true,countrysource=GPS)
2020-08-16 10:31:37.643 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: deviceLocation = DeviceLocation(userId=user,countrysource=GPS)
2020-08-16 10:31:37.668 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: user = User(userKey=user,userId=108547,passfrase=eb7212853b9c14ca2975a39edc09cd5f,currentCountry=,currentVersion=v3.2.42.108.4915 debug,tmsSIMCountryISO=us,tmsNWCountryISO=us,apiServiceKey=AIzaSyD1cbX6eQDA15ZRAFUKaZUOYKco_tkez3M)
2020-08-16 10:31:37.684 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: userSelection = UserSelection(userId=user,currentKindId=0,currentStationId=null,showHidden=false,showNotAvailable=false,showUncertain=true,showNotValid=true,showUnknown=true,showOlder=true,showOld=true,showFair=true,showNew=true,showDetailNotAvailable=true,showDetailUncertain=true,showDetailPresent=true,selectionArea=Global,selectionSorting=Nearest,selectionCountry=null,selectionAutoCountry=true,addressCheckText=null,currentRange=1.0)
2020-08-16 10:31:37.685 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: resets range to 1.0
2020-08-16 10:31:37.687 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: range = 1.0
2020-08-16 10:31:37.687 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: countryCode = NO
2020-08-16 10:31:37.687 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: userSelection.selectionArea = Global
2020-08-16 10:31:37.687 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: userSelection.selectionSorting = Nearest
2020-08-16 10:31:37.687 30686-30686/no.rogo.emptyfuel I/StationListRemoteMediator2: load: global,range = 1.0
2020-08-16 10:31:37.694 30686-31115/no.rogo.emptyfuel I/okhttp.OkHttpClient: --> GET https://www.famapp.no/fuelpump/liststations_fp3_v6.php?userid=yyyyy&passfrase=xxxxxxxxxxxxxxxxxx&latitude=61.8907267&longitude=6.6761067&limit=20&offset=0&lastversion=v3.2.42.108.4915%20debug&killed=0&range=1.0
2020-08-16 10:31:37.694 30686-31115/no.rogo.emptyfuel I/okhttp.OkHttpClient: --> END GET
2020-08-16 10:31:37.706 30686-30686/no.rogo.emptyfuel I/StationListFragment: subscribeUi: viewModelJob collecting flow
2020-08-16 10:31:37.708 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: .addLoadStateListener combinedLoadStates = CombinedLoadStates(source=LoadStates(refresh=Loading(endOfPaginationReached=false),append=NotLoading(endOfPaginationReached=false)))
2020-08-16 10:31:37.708 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: combinedLoadStates.refresh = Loading(endOfPaginationReached=false)
2020-08-16 10:31:37.773 30686-31115/no.rogo.emptyfuel I/okhttp.OkHttpClient: <-- HTTP FAILED: java.io.IOException: Canceled
2020-08-16 10:31:37.785 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: .addLoadStateListener combinedLoadStates = CombinedLoadStates(source=LoadStates(refresh=NotLoading(endOfPaginationReached=false),append=NotLoading(endOfPaginationReached=false)))
2020-08-16 10:31:37.786 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: combinedLoadStates.refresh = Loading(endOfPaginationReached=false)
2020-08-16 10:31:37.786 30686-30686/no.rogo.emptyfuel I/StationListFragment: adapter: .addLoadStateListener combinedLoadStates = CombinedLoadStates(source=LoadStates(refresh=NotLoading(endOfPaginationReached=false),prepend=NotLoading(endOfPaginationReached=false),append=NotLoading(endOfPaginationReached=false)))
上面的日志中的注意,用户名和密码已被yy和xx故意遮盖了
RG
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)