简体   繁体   English

适用于Retrofit 2的自定义CallAdapter和Android上的线程问题

[英]Custom CallAdapter for Retrofit 2 and threading issue on Android

In Retrofit2, when I use a custom CallAdapter for error handling that Retrofit provides on samples/ErrorHandlingAdapter.java , the callback methods executed on a background thread instead of the main thread, unlike the default CallAdapter (Call), which is executed on the main thread. 在Retrofit2中,当我使用Retrofit在samples / ErrorHandlingAdapter.java上提供的自定义CallAdapter进行错误处理时,与在主线程上执行的默认CallAdapter(Call)不同,在后台线程而不是主线程上执行的回调方法线。 I made sure of that by running Thread.currentThread().getName() on both 我通过在两者上运行Thread.currentThread()。getName()来确保这一点

This is a big problem for me. 对我来说这是一个大问题。 I don't want to use the runOnUiThread method every time I want to do something in ui-thread. 我不想每次在ui线程中执行某些操作时都使用runOnUiThread方法。

The source code of ErrorHandlingAdapter mentioned above: 上面提到的ErrorHandlingAdapter的源代码:

/*
 * Copyright (C) 2015 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.retrofit;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.Executor;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.http.GET;

/**
 * A sample showing a custom {@link CallAdapter} which adapts the built-in {@link Call} to a custom
 * version whose callback has more granular methods.
 */
public final class ErrorHandlingAdapter {
  /** A callback which offers granular callbacks for various conditions. */
  interface MyCallback<T> {
    /** Called for [200, 300) responses. */
    void success(Response<T> response);
    /** Called for 401 responses. */
    void unauthenticated(Response<?> response);
    /** Called for [400, 500) responses, except 401. */
    void clientError(Response<?> response);
    /** Called for [500, 600) response. */
    void serverError(Response<?> response);
    /** Called for network errors while making the call. */
    void networkError(IOException e);
    /** Called for unexpected errors while making the call. */
    void unexpectedError(Throwable t);
  }

  interface MyCall<T> {
    void cancel();
    void enqueue(MyCallback<T> callback);
    MyCall<T> clone();

    // Left as an exercise for the reader...
    // TODO MyResponse<T> execute() throws MyHttpException;
  }

  public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
    @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit) {
      if (getRawType(returnType) != MyCall.class) {
        return null;
      }
      if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalStateException(
            "MyCall must have generic type (e.g., MyCall<ResponseBody>)");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
      Executor callbackExecutor = retrofit.callbackExecutor();
      return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
    }

    private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R, MyCall<R>> {
      private final Type responseType;
      private final Executor callbackExecutor;

      ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
        this.responseType = responseType;
        this.callbackExecutor = callbackExecutor;
      }

      @Override public Type responseType() {
        return responseType;
      }

      @Override public MyCall<R> adapt(Call<R> call) {
        return new MyCallAdapter<>(call, callbackExecutor);
      }
    }
  }

  /** Adapts a {@link Call} to {@link MyCall}. */
  static class MyCallAdapter<T> implements MyCall<T> {
    private final Call<T> call;
    private final Executor callbackExecutor;

    MyCallAdapter(Call<T> call, Executor callbackExecutor) {
      this.call = call;
      this.callbackExecutor = callbackExecutor;
    }

    @Override public void cancel() {
      call.cancel();
    }

    @Override public void enqueue(final MyCallback<T> callback) {
      call.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, Response<T> response) {
          // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
          // on that executor by submitting a Runnable. This is left as an exercise for the reader.

          int code = response.code();
          if (code >= 200 && code < 300) {
            callback.success(response);
          } else if (code == 401) {
            callback.unauthenticated(response);
          } else if (code >= 400 && code < 500) {
            callback.clientError(response);
          } else if (code >= 500 && code < 600) {
            callback.serverError(response);
          } else {
            callback.unexpectedError(new RuntimeException("Unexpected response " + response));
          }
        }

        @Override public void onFailure(Call<T> call, Throwable t) {
          // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
          // on that executor by submitting a Runnable. This is left as an exercise for the reader.

          if (t instanceof IOException) {
            callback.networkError((IOException) t);
          } else {
            callback.unexpectedError(t);
          }
        }
      });
    }

    @Override public MyCall<T> clone() {
      return new MyCallAdapter<>(call.clone(), callbackExecutor);
    }
  }
}

In Android, I added the ErrorHandlingAdapter to Retrofit before doing any calls: 在Android中,我在执行任何调用之前将ErrorHandlingAdapter添加到Retrofit:

// Initializing retrofit
BooleanTypeAdapter typeAdapter = new BooleanTypeAdapter();
    gson = new GsonBuilder().setLenient().registerTypeAdapter(boolean.class, typeAdapter).create();
    apiService = new Retrofit.Builder().baseUrl(BASE_API_URL)
        .addCallAdapterFactory(new ErrorHandlingCallAdapterFactory())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
        .create(ApiService.class);

You're going to have to deal with it being on the other thread, that's just the nature of asynchronous libraries. 您将不得不在另一个线程上处理它,这只是异步库的本质。 If you only have the one error handler, then it's not too big a burden to make the needed threading calls. 如果只有一个错误处理程序,那么进行所需的线程调用就不会有太大的负担。 If you have multiple, then you could make a lightweight delegating implementation and reuse. 如果您有多个,则可以进行轻量级的委派实现和重用。

Your other alternative is to dive into using an rxjava implementation which gives precise threading control. 您的另一种选择是研究使用提供精确线程控制的rxjava实现。

To get callback on main thread you should use the callbackExecutor provided in MyCallAdapter. 要获得主线程的回调应该使用callbackExecutor在MyCallAdapter提供。

callbackExecutor.execute(new Runnable() {
                    @Override
                    public void run() {

                        int code = response.code();
                        if (code >= 200 && code < 300) {
                            callback.success(response);
                        } else if (code == 401) {
                            callback.unauthenticated(response);
                        } else if (code >= 400 && code < 500) {
                            callback.clientError(response);
                        } else if (code >= 500 && code < 600) {
                            callback.serverError(response);
                        } else {
                            callback.unexpectedError(new RuntimeException("Unexpected response " + response));
                        }
                    }
                });

Sample has a TODO written: 样本中写有一个TODO:

// TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed // on that executor by submitting a Runnable. // TODO如果'callbackExecutor'不为null,则应通过提交Runnable在该执行程序上执行'callback'方法。 This is left as an exercise for the reader. 这留给读者练习。

It is an Retrofit callback executor which runs on UI thread in android. 它是Retrofit回调执行器,可在android中的UI线程上运行。

See here & here 在这里这里看到

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM