SubscribeOn(Schedulers.io())+ blockingGet()= UI跳过的帧

时间:2017-11-12 16:40:24

标签: android retrofit2 rx-java2

我为远程API编写了一个TokenManager的RxJava实现(我通过Retrofit消费)。但是,即使我blockingGet()

,我遇到了subscribeOn(Schedulers.io())方法调用导致跳过的UI框架的问题。

基本上,我已将getToken()作为参数包含在API search()调用方法中。如果该令牌存在,则将提供该令牌,如果不存在,则将通过API token()调用获取该令牌。 < - 这就是问题所在。当调用此方法时,它会导致UI中跳过的帧(进度条暂时冻结+相应的logcat Choreographer skipped frames!!消息)

寻找有关如何纠正跳过的帧的建议,或者有关如何更好地实现此代码的建议。

ListFetcher(调用TokenManager getToken()的类)

public Single<List<Business>> getList(final String latitude, final String longitude) {
    return api
            .search(
                    tokenManager.getToken(),  // <-- Here's the TokenManager reference
                    AppSettings.SEARCH_TERM,
                    latitude,
                    longitude,
                    AppSettings.SEARCH_RADIUS,
                    Yelp3Api.SEARCH_LIMIT)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap(searchResponse -> {
                if (searchResponse.getBusinesses().size() < searchResponse.getTotal()) {
                    return subsequentSearchCalls(searchResponse, latitude, longitude)
                            .map(businesses -> {
                                List<Business> list = new ArrayList<>();
                                list.addAll(searchResponse.getBusinesses());
                                list.addAll(businesses);
                                return list;
                            });
                } else {
                    return Single.just(searchResponse.getBusinesses());
                }
            });
}

TokenManager getToken()

public synchronized String getToken() {
    final String cachedToken = sharedPrefs.getString(tokenKey, "null");
    if (cachedToken.equals("null")) {
        String tokenString = api
                .token(
                        Yelp3Api.GrantType.CLIENT_CREDENTIALS,
                        BuildConfig.YELPFUSION_CLIENT_ID,
                        BuildConfig.YELPFUSION_CLIENT_SECRET)
                .subscribeOn(Schedulers.io())
                .doOnSuccess(this::setSharedPrefToken)
                .map(tokenResponse -> String.format(AUTH_FORMAT, tokenResponse.getAccessToken()))
                .blockingGet();
        return tokenString;
    } else {
        return String.format(AUTH_FORMAT, cachedToken);
    }
}

Yelp3Api(改造界面)

@FormUrlEncoded
@POST("oauth2/token")
Single<TokenResponse> token(
        @Field("grant_type") String grantType,
        @Field("client_id") String clientId,
        @Field("client_secret") String clientSecret
);

@GET("v3/businesses/search")
Single<SearchResponse> search(
        @Header("Authorization") String authorization,
        @Query("term") String term,
        @Query("latitude") String latitude,
        @Query("longitude") String longitude,
        @Query("radius") int radius,
        @Query("limit") int limit
);

2 个答案:

答案 0 :(得分:2)

当你致电getToken()时,它不会被懒散地执行。它在您的UI线程上执行阻塞调用。 要解决此问题,请将Single.fromCallable中的getToken和flatMap上的api.search()包裹起来

希望这能清除实际发生的事情

答案 1 :(得分:0)

我最后使用okhttp authenticator重新编写此请求以获取身份验证凭据,并使用okhttp interceptor将auth标头添加到传出请求中。

根据the internet,这似乎是一个更好的实施。

但我接受@ Tuby的答案,因为这是对原始问题的更直接的解决方法。