您的位置:首页 > 移动开发 > Android开发

Android 使用Retrofit2问题汇总

2016-05-23 15:54 405 查看

Retrofit2引用依赖问题

compile 'io.reactivex:rxjava:1.1.3'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:converter-scalars:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.8.0'


在这里遇到一些坑,如果下面三个少一个,返回他不支持的结果时,就会崩溃,这也是上面为什么会导那么多包的原因。

private static Retrofit getRetrofit(String url) {
return new Retrofit.Builder().baseUrl(url)
//增加返回值为String的支持
.addConverterFactory(ScalarsConverterFactory.create())
//增加返回值为Gson的支持(以实体类返回)
.addConverterFactory(GsonConverterFactory.create())
//增加返回值为Oservable<T>的支持
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}


GET请求

http://wthrcdn.etouch.cn/weather_mini?city=深圳

public interface IWeather {
@GET("/weather_mini")
Observable<Weather> getWeather(@Query("city") String city);
}


public static final String url_weather = "http://wthrcdn.etouch.cn";
public static IWeather getIWeather() {
return getRetrofit(url_weather).create(IWeather.class);
}


HttpUtil.getIWeather().getWeather("深圳")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Weather>() {
@Override
public void call(Weather weather) {
tv.setText(weather.getData().getGanmao());
}
});


Post请求

根据后台,Multipart或FormUrlEncoded,如果不知道最好两种都试一下,我在这里也是遇了大坑,如果方式错了,根本就post不上去。。

Multipart

http://122.114.38.95:8857 (这个是用的自己的服务器,后面的人看到这篇博客可能这个api就用不了了)

参数是 action , 如果action=banner就可以返回正确结果否则错误结果

@Multipart
@POST("/")
Observable<String> getBanner(@Part("action") String action);


public static final String url_bannertest = "http://122.114.38.95:8857";
public static IBannerTest getBanner() {
return getRetrofit(url_bannertest).create(IBannerTest.class);
}


HttpUtil.getBanner().getBanner("banner")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
tv.setText(s);
}
});


post上传图片

在这里是用了image++的接口。

http://apicn.imageplusplus.com/analyze?

参数:api_key,api_secret, img

@Multipart
@POST("/analyze")
Observable<String> upLoadImage( @Part("api_key") String api_key, @Part ("api_secret") String api_secret,@Part MultipartBody.Part file );


public static final String imagepp_url = "http://apicn.imageplusplus.com";
public static IImagePP getIImagePP() {
return getRetrofit(imagepp_url).create(IImagePP.class);
}

public static MultipartBody.Part postFileParams(String key, File file) {
RequestBody fileBody = RequestBody.create(MediaType.parse("image/*"), file);
return MultipartBody.Part.createFormData(key, file.getName(), fileBody);
}


File file = new File(Environment.getExternalStorageDirectory()+"/123.png");
HttpUtil.getIImagePP().upLoadImage("c1a2b3ab56a2f218aed9b2ab3c16ce88","be8318b73cef1c2bcafb6c8a77922436",HttpUtil.postFileParams("img", file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
tv.setText(s);
}
});


这里用到了okhttp的特性,这也是为什么要导入okhttp的原因。

private static Retrofit getRetrofit(String url) {
return new Retrofit.Builder().baseUrl(url)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}


上面说到了Retrofit加入了3句。

ScalarsConverterFactory是支持返回String类型

GsonConverterFactory是支持返回实体类。

RxJavaCallAdapterFactory是支持返回rxJava的Observable。

上面的例子Get请求用到了GsonConverterFactory和RxJavaCallAdapterFactory。

Post用到了ScalarsConverterFactory和RxJavaCallAdapterFactory

少一个都会蹦,所以大家用retrofit需要注意这一点。

线程(Scheduler)

Scheduler是RxJava的线程调度器,可以指定代码执行的线程。RxJava内置了几种线程:

AndroidSchedulers.mainThread() 主线程

Schedulers.immediate() 当前线程,即默认Scheduler

Schedulers.newThread() 启用新线程

Schedulers.io() IO线程,内部是一个数量无上限的线程池,可以进行文件、数据库和网络操作。

Schedulers.computation() CPU计算用的线程,内部是一个数目固定为CPU核数的线程池,适合于CPU密集型计算,不能操作文件、数据库和网络。

在实际项目中,Schedulers选择 newThread 还是io呢?

一般的网络请求应该使用io,因为io使用了无限的线程池,而newThread没有线程池维护

参考:/article/11842008.html

onNext方法出现异常

Observer的onNext方法被调用后,它的onError方法也紧跟着被调用了……我的第一反应是RxJava的Bug?……

当然不是了

@Override
public void onNext(T args) {
try {
if (!done) {
actual.onNext(args);
}
} catch (Throwable e) {
// we handle here instead of another method so we don't add stacks to the frame
// which can prevent it from being able to handle StackOverflow
Exceptions.throwIfFatal(e);
// handle errors if the onNext implementation fails, not just if the Observable fails
onError(e);
}
}


// handle errors if the onNext implementation fails, not just if the Observable fails

当onNext里我们的函数发生异常时,onError会被调用

API注解

所有请求都需要一个请求方法注解并以相对URL路径作为参数。内建了5个注解:GET, POST, PUT, DELETE, and HEAD

@Path与@Query

@GET("{username}")
Call<User> getUser(@Path("username") String username);


@POST("users")
Call<List<User>> getUsersBySort(@Query("sort") String sort);


path是可以动态替换的,通过代码一比较就知道区别了。

@map注解

复杂的查询采用@map:

interface QueryGET{
@GET("/sheet")
String getString(@Query("name")String name,@Query("age") int age,@QueryMap(encodeNames=true) Map<String, String> filters);
}


@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);


REQUEST BODY请求体

@POST("/users/new")
void createUser(@Body User user, Callback<User> cb);


注意采用一个POJO作为请求体,它会被RestAdapter进行转换。同时POST方式可以传入回调。

FORM ENCODED AND MULTIPART表单域与文件上传

@FormUrlEncoded修饰表单域,每个表单域子件key-value采用@Field修饰

@FormUrlEncoded
@POST("/user/edit")
User updateUser(@Field("first_name") String first, @Field("last_name") String last);


@Multipart修饰用于文件上传,每个Part元素用@Part修饰:

@Multipart
@PUT("/user/photo")
User updateUser(@Part("photo") TypedFile photo, @Part("description") TypedString description);


Header注解

@Headers("Cache-Control: max-age=640000")
@GET("/widget/list")
List<Widget> widgetList();


@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("/users/{username}")
User getUser(@Path("username") String username);


@GET("/user")
void getUser(@Header("Authorization") String authorization, Callback<User> callback)


OkHttp 3.0 配置

必须依赖:

compile 'com.squareup.okhttp3:okhttp:3.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'


完整配置:

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.addNetworkInterceptor(mTokenInterceptor)
.build();


HttpLoggingInterceptor 是一个拦截器,用于输出网络请求和结果的 Log,可以配置 level 为 BASIC / HEADERS / BODY,都很好理解,对应的是原来 retrofit 的 set log level 方法,现在 retrofit 已经没有这个方法了,所以只能到 OkHttp 这边来配置,并且 BODY 对应原来到 FULL.

retryOnConnectionFailure 方法为设置出现错误进行重新连接。

connectTimeout 设置超时时间

addNetworkInterceptor 让所有网络请求都附上你的拦截器,我这里设置了一个 token 拦截器,就是在所有网络请求的 header 加上 token 参数,下面会稍微讲一下这个内容。

让所有网络请求都附上你的 token:

Interceptor mTokenInterceptor = new Interceptor() {
@Override public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (Your.sToken == null || alreadyHasAuthorizationHeader(originalRequest)) {
return chain.proceed(originalRequest);
}
Request authorised = originalRequest.newBuilder()
.header("Authorization", Your.sToken)
.build();
return chain.proceed(authorised);
}
};


解释:

那个 if 判断意思是,如果你的 token 是空的,就是还没有请求到 token,比如对于登陆请求,是没有 token 的,只有等到登陆之后才有 token,这时候就不进行附着上 token。另外,如果你的请求中已经带有验证 header 了,比如你手动设置了一个另外的 token,那么也不需要再附着这一个 token.

header 的 key 通常是 Authorization,如果你的不是这个,可以修改。

如果你需要在遇到诸如 401 Not Authorised 的时候进行刷新 token,可以使用 Authenticator,这是一个专门设计用于当验证出现错误的时候,进行询问获取处理的拦截器:

Authenticator mAuthenticator = new Authenticator() {
@Override public Request authenticate(Route route, Response response)
throws IOException {
Your.sToken = service.refreshToken();
return response.request().newBuilder()
.addHeader("Authorization", Your.sToken)
.build();
}
}


然后,对于以上的两个拦截器,分别使用 OkHttpClient.Builder 对象的 addNetworkInterceptor(mTokenInterceptor) 和 authenticator(mAuthenticator) 即可。

Retrofit配置

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConfig.BASE_URL)
.client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
service = retrofit.create(YourApi.class);


参考:https://drakeet.me/retrofit-2-0-okhttp-3-0-config
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: