Okhttp3 retroift2 GET调用在具有暂停加载实现的androidx分页3实现中被取消

问题描述

我使用androidx分页3实现,遇到了一个奇怪的问题。

在某些情况下,我的“ okhttp3 retrofit2 https GET请求”被意外取消了。

我的简单问题是:OkHttpClient何时意外取消请求? (我没有从任何地方发送任何取消请求。)

  1. 第二次发送同一请求时是否存在(如果我执行“刷新”请求,则最有可能发生。由于服务器中的预期更改,IE第二次将同一请求发送到服务器,但没有更改完成,所以结果是相同的。)
  2. 对服务器的并发请求是否有限制。
  3. 远程服务器可以取消吗?
  4. 如果请求是在请求响应内死掉的线程中完成的,则可以取消该请求,如果是的话,我如何防止它发生(这很奇怪...,该请求在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 (将#修改为@)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...