在Anrdoid中,有很多种Http通讯网络库可供我们使用,比如Volley, OKHttp和Retrofit等,没有最好的,只有最适合的。在不同的场景下,我们会有不同的选择。
如果只是单纯地从服务端的restful api接口中获取json格式的数据,相信很多人都会选择Retrofit,因为其实现起来最简单,最方便。
但是如果还需要从服务端获取图片,那相信很多人会说Volley会做得更好啊,因为Retrofit没有现成的接口,而Volley有NetworkImageView或者ImageRequest等,但可能也会有人说,直接用Glide和Picasso会更舒服呀。
目前都是用Android Studio 来进行开发的,依赖什么的都是通过 gradle来配置,所以按照以下几步走:
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'
其中converter-gson,是retrofit2提供的,将Response的Body转化为json对象的方法,也就是说Retrofit2提供了关于Json的转化,我们不需要自己再去做这个处理了。
在导入任何第三方包的时候,我第一个总会去找其Proguard配置是什么。一般来说,官方提供的文档中会告诉我们如何使用这些库,也会提供一段Proguard配置来让我们在应用中进行配置
#retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
在这里,其实有一点要注意的,-keepattributes Signature,是为了Gson配置的,是因为Gson在转化Field(字段)的时候会使用到存储在类中泛型信息,而Proguard默认会将这些信息给移除掉,所以我们需要Keep住这些Signature。Signature 是字段,方法和类的签名信息,用来标识其唯一性的。同一个方法,参数不同,如何区分,其实就是靠Signature。
声明我们需要与服务器进行通讯的请求,包括请求类型,请求地址,请求参数,参数的形式等。
public interface TestService {
@FormUrlEncoded
@POST("http://ip:port/api/login")
Call login(@Field("username") String username,
@Field("password") String password);
}
注释 @FormUrlEncoded 指明了请求的内容(content)会使用 form-encoded 的形式,而我们想要传递给服务器的参数,则必须用 @Field 来指定,如上述方法,指定了login方法会指向服务器的http://ip:port/api/login 接口,请求类型是 Post,指定参数为 username 和 password,而Call 则是Retrofit 定义的一次发送Request并接收Response的一个接口,可以将其看作是一次请求的触发。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SharedPreferenceHelper.getServerHost())
.addConverterFactory(GsonConverterFactory.create())
.client(buildClient())
.build();
TestService service = retrofit.create(TestService.class);
通过Retrofit.Builder.build() 方法,我们可以创建Retrofit对象,再通过Retrofit对象,我们创建TestService对象,从而真正地将 TestService 接口实例化。
.baseUrl("http://192.168.1.100")
则我们指定了服务器地址为 http://192.168.1.100,这样,我们在声明接口TestService的api时,就可以只声明路径,如下:
@FormUrlEncoded
@POST("/api/login")
Call login(@Field("username") String username,
@Field("password") String password);
这就是我们在 build.gradle中引入的 converter-gson 使用的地方了。
public class LoggingInterceptor implements Interceptor{
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Logger.v("request:" + request.toString());
if (request.body() != null) {
FormBody body = (FormBody) request.body();
int size = body.size();
for (int i = 0; i < size; i++) {
Logger.v(body.name(i) + " : " + body.value(i));
}
}
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Logger.v(String.format("received " + response.toString() + " in %.1fms%n", (t2 - t1) / 1e6d));
return response;
}
}
在这里,通过 FormBody(因为我们使用的是 Form-Encoded的方法)来将RequestBody其中的参数和值给打印出来,同样的,我理所当然地以为,也可以将response.body也可以打印出来,发现可以通过response.body.string() 方法,可以将response的内容给打印出来,比如这样
if (response.body() != null) {
Logger.v("response.body: " + response.body().string());
}
结果,真的打印出来了,如我所愿,然而,踩坑了。
!!,在这里有个坑,不能使用 response.body.string() 方法, 因为 string() 方法会将response的内容给读取了,导致整个请求关闭,这样在后续操作,就会收到Exception: close了。
定义了 LoggingInterceptor,创建OKHttpClient,如下:
private static OkHttpClient buildClient() {
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
return client;
}
再通过 .client(buildClient)方法,设置给Retrofit,达到在TestService调用方法的时候进行拦截。
private void doLogin() {
Call call = testService.login("username","password");
call.enqueue(new Callback() {
@Override
public void onResponse(Response response) {
LoginResult loginResult = response.body();
//do something about loginResult
}
@Override
public void onFailure(Throwable t) {
//do something about throwable t
}
});
}
通过 call.enqueue方法,将call放进请求队列里,并声明一个Callback,通过callback的onResponse方法和onFailure方法,分别对正常的Response和请求过程中出现的错误进行处理。
对Retrofit2的基本使用,到此就结束了。
参考并感谢
高效率http retrofit okhttp
Retrofit
Interceptors