retrofit/CHANGELOG.md at master · square/retrofit
retrofit との後方互換性はありません。そのため maven の group id が com.squareup.retrofit2 になっています。
compile 'com.squareup.retrofit2:retrofit:2.0.0'
Converter
GSON などの Converter は別のモジュールに分割されました。
retrofit
new RestAdapter.Builder()
.setConverter(new GsonConverter(gson))
...
retrofit2
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
...
gson 以外の Converter については square.github.io/retrofit/ の CONVERTERS 部分に記載があります。
RxJava
retrofit で RxJava を使うためのクラスは別のモジュールに分割されました。
retrofit2
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
...
AndroidLog
retrofit.android.AndroidLog はなくなりました。 https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
を使って okhttp3 の interceptor で行います。
retrofit
AndroidLog logger = new AndroidLog(MyService.class.getSimpleName());
new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(logger)
...
retrofit2
compile 'com.squareup.okhttp3:logging-interceptor:3.0.0'
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
new OkHttpClient.Builder();
.addInterceptor(logging)
...
ErrorHandler, RetrofitError
retrofit.ErrorHandler, retrofit.RetrofitError はなくなりました。
okhttp3 の interceptor で行うとか https://github.com/square/retrofit/issues/1102
CallAdapterFactory を作るとか https://github.com/square/retrofit/pull/1277/files
いくつか方法があります。
adapter-rxjava を使っているなら HttpException が onError() で返されるので、ここから status code, message, response が取れます。
retrofit
new RestAdapter.Builder()
.setErrorHandler(new MyErrorHandler())
...
public class MyErrorHandler implements ErrorHandler {
@Override
public Throwable handleError(RetrofitError cause) {
if (cause.getKind() == RetrofitError.Kind.NETWORK) {
// network error
...
} else if (cause.getResponse() != null) {
final int statusCode = cause.getResponse().getStatus();
try {
MyErrorResponse errorResponse =
(MyErrorResponse) cause.getBodyAs(MyErrorResponse.class);
} catch (Exception e) {
...
}
} else {
...
}
}
}
retrofit2
@Override
public void onError(Throwable e) {
if (e instanceof IOException) {
// network error
...
} else if (e instanceof HttpException) {
HttpException he = (HttpException) e;
final int statusCode = he.code();
final ResponseBody errorBody = e.response().errorBody();
if (errorBody != null) {
try {
MyErrorResponse errorResponse =
(MyErrorResponse) GsonConverterFactory
.create()
.responseBodyConverter(MyErrorResponse.class,
new Annotation[0], null)
.convert(errorBody);
// retrofit インスタンスが利用できる場合
// MyErrorResponse errorResponse = retrofit
// .responseConverter(MyErrorResponse.class, new Annotation[0])
// .convert(errorBody);
} catch (Exception re) {
...
}
}
} else {
...
}
}
RequestInterceptor
retrofit.RequestInterceptor はなくなりました。
okhttp3 の interceptor を使います。https://github.com/square/retrofit/issues/1082
retrofit
public class MyRequestInterceptor implements RequestInterceptor {
@Override
public void intercept(RequestFacade request) {
request.addHeader(HEADER_KEY, headerValue);
}
}
new RestAdapter.Builder()
.setRequestInterceptor(new MyRequestInterceptor())
...
retrofit2
public class MyRequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
final Request.Builder builder = chain.request().newBuilder();
builder.addHeader(HEADER_KEY, headerValue);
return chain.proceed(builder.build());
}
}
new OkHttpClient.Builder()
.addInterceptor(new MyRequestInterceptor())
...
Response
import 先を変更する必要があります。
retrofit
import retrofit.client.Response
retrofit2
import retrofit2.Response
Observable<Response>
ResponseBody を指定する必要があります。
retrofit
@DELETE("/delete")
Observable<Response> delete();
retrofit2
@DELETE("/delete")
Observable<Response<ResponseBody>> delete();
@Body, @GET, @Post など
import 先を変更する必要があります。
retrofit
import retrofit.http.Body
retrofit2
import retrofit2.http.Body
Query の encodeValue は encoded に名前が変わっています。
retrofit
@Query(value = "categories", encodeValue = false) @Nullable String categories
retrofit2
@Query(value = "categories", encoded = false) @Nullable String categories
TypedFile
TypedFile はなくなりました。
MultipartBody.Part を使います。
retrofit
@Multipart
@POST("/upload")
Observable<Response> uploadImage(
@Part("image") TypedFile image
);
final TypedFile image = new TypedFile("image/jpeg", file);
retrofit2
@Multipart
@PATCH("/upload")
Observable<Response> uploadImage(
@Part() MultipartBody.Part image
);
MultipartBody.Part のときは @Part に value(上記だと "image")を設定してはいけません。設定すると実行時に落ちます。
final MultipartBody.Part image = MultipartBody.Part.createFormData("image",
file.getName(),
RequestBody.create(MediaType.parse("image/jpeg"), file));
もともと @Part に指定していた "image" は MultipartBody.Part.createFormData() の第1引数に指定します。
Client
retrofit.client.Client はなくなりました。
retrofit側でモック化したい場合は retrofit-mock があります。
compile 'com.squareup.retrofit2:retrofit-mock:2.0.0'
使い方は
https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleMockService.java が参考になります。
okhttp側でモック化したい場合は mockwebserverがあります。
compile 'com.squareup.okhttp3:mockwebserver:3.0.0'
okhttp
okhttp3 を使います。
retrofit
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
retrofit2
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
stetho-okhttp
stetho-okhttp3 を使います。https://github.com/facebook/stetho/issues/327
retrofit2
compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
java.io.InterruptedIOException: thread interrupted
Retrofit2 (というか okhttp3)にして
thread interrupted
java.io.InterruptedIOException: thread interrupted
at okio.Timeout.throwIfReached(Timeout.java:145)
at okio.Okio$1.write(Okio.java:77)
...
というようなエラーが起きたら、RxJava の subscribeOn() や observeOn() の使い方があやしいので見直しましょう。
例えば以下のような、subscribeOn(Schedulers.newThread()) を指定した複数の Observable を merge して takeLast すると InterruptedIOExceptionになります。 zip や combineLatest だと問題ないです。
NG
final List<Observable<ImageResponse>> observables = new ArrayList<>();
for (...) {
final MultipartBody.Part image = ...;
final Observable<ImageResponse> o = service.uploadImage(image)
.subscribeOn(Schedulers.newThread());
observables.add(o);
}
Observable
.merge(observables)
.takeLast(1)
.subscribeOn(Schedulers.newThread());
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ImageResponse>() {
...
});
OK
final List<Observable<ImageResponse>> observables = new ArrayList<>();
for (...) {
final MultipartBody.Part image = ...;
final Observable<ImageResponse> o = service.uploadImage(image)
.subscribeOn(Schedulers.newThread());
observables.add(o);
}
Observable
.combineLatest(observables, new FuncN<ImageResponse>() {
@Override
public ImageResponse call(Object... args) {
return ...;
}
})
.subscribeOn(Schedulers.newThread());
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ImageResponse>() {
...
});