2017-05-14 45 views
1

在Retrofit2中,當我使用自定義的CallAdapter進行錯誤處理時,Retrofit在samples/ErrorHandlingAdapter.java上提供了回調方法,它在後臺線程而不是主線程上執行,與默認的CallAdapter調用),這是在主線程上執行的。我通過在Custom CallAdapter for Retrofit 2和Android上的線程問題

上運行Thread.currentThread()。getName()來確保這是一個很大的問題。我不想每次我想在ui-thread中執行某些操作時使用runOnUiThread方法。

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); 
    } 
    } 
} 

在Android中,我加入了ErrorHandlingAdapter到做任何調用之前改造:

// 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); 

回答

1

要在主線程上獲得回調,您應該使用MyCallAdapter中提供的callbackExecutor

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)); 
         } 
        } 
       }); 

樣本已經寫了一個TODO:

//如果 'callbackExecutor' 不爲空TODO的 '回調' 方法應通過提交一個Runnable執行 //上執行。這留給讀者作爲練習。

這是一個在Android的UI線程上運行的Retrofit回調執行程序。

See here & here

1

你將不得不對付它在另一個線程上,這就是異步庫的本質。如果你只有一個錯誤處理程序,那麼進行所需的線程調用並不是一個太大的負擔。如果你有多個,那麼你可以做一個輕量級委託實現和重用。

你的另一種選擇是深入使用rxjava實現,它提供了精確的線程控制。