发送网络请求
APP的联网能力现在必不可少, 所以选择一个适合的网络框架至关重要. 我主要以以下几个方面考虑:
使用简单简洁
方便的自定义, 如加请求头
方便的取消请求
能方便的与GSON等序列化库结合
完善的Log信息打印
Retrofit2
最后选择了Retrofit2, 具体介绍可以参考 用 Retrofit 2 简化 HTTP 请求. 基本用法可以参考 官方网站.
这里就不再赘述Retrofit2的优缺点, 主要谈谈引入Retrofit2后需要做的一些准备工作:
将Retrofit2的API应用至Model层
与GSON结合
自定义Log输出
处理一些通用的异常
取消请求
预处理请求结果
1. 将Retrofit2的API应用至Model层
Retrofit的特色是使用注解与接口来描述网络请求, 在多数情况下能很好的与Model层的接口结合起来, 而无需重写实现类.
public interface HomepageApi {
@POST(Urls.GET_TYPES)
Call<BaseData> getTypes();
@FormUrlEncoded
@POST(Urls.GET_HOT_PRODUCTS)
Call<BaseData> getTopProducts(@Field("pageNo") int pageNo,
@Field("pageSize") int pageSize);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Urls.BaseUrl)
.build();
HomepageApi api = retrofit.create(HomepageApi.class);
然后通过api对象去调用getTypes或getTopProducts接口. 通过这种方式我们就能直接将描述请求的接口应当做一个Model来使用. 但是每次使用都去要生成一个Retrofit.Builder, 然后做一些配置, 额外增加了重复代码, 因此我们可以写一个工具类, 来专门生成Retrofit接口实例
在这里我使用了两个类:
RetrofitFactory 持有一个Retrofit单例, 专门用于配置Retrofit, 比如baseUrl, converter, adapter之类.
ApiFactory 单例, 持有一个HashMap对象, 用于缓存Retrofit生成的接口实例, 而无需重复生成.
/**
* 用于获取配置好的retrofit对象, 通过设置{@link Configuration#enableLoggingNetworkParams()}来启用网络请求
* 参数与相应结果.
*/
public class RetrofitFactory {
private static Retrofit retrofit;
private static String baseUrl;
public static void setBaseUrl(String url) {
baseUrl = url;
}
/**
* 获取配置好的retrofit对象来生产Api对象
*/
public static Retrofit getRetrofit() {
if (retrofit == null) {
if (baseUrl == null || baseUrl.length() <= 0)
throw new IllegalStateException("请在调用getFactory之前先调用setBaseUrl");
Retrofit.Builder builder = new Retrofit.Builder();
builder.baseUrl(baseUrl)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 参考RxJava
.addConverterFactory(GsonConverterFactory.create()); // 参考与GSON的结合
// 参考自定义Log输出
if (Configuration.isShowNetworkParams()) {
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(
new HttpLoggingInterceptor()).build();
builder.client(client);
}
retrofit = builder.build();
}
return retrofit;
}
}
/**
* 通过定义好的api接口以及Retrofit来生成具体的实例.
*/
public class ApiFactory {
private static ApiFactory factory;
private static HashMap<String, Object> serviceMap = new HashMap<>();
public static ApiFactory getFactory() {
if (factory == null) {
synchronized (ApiFactory.class) {
if (factory == null)
factory = new ApiFactory();
}
}
return factory;
}
public <T> T create(Class<T> clz) {
Object service = serviceMap.get(clz.getName());
if (service == null) {
service = RetrofitFactory.getRetrofit().create(clz);
serviceMap.put(clz.getName(), service);
}
return (T) service;
}
}
HomepageApi api = ApiFactory.getFactory().create(HomepageApi.class);
在使用前还需要调用
RetrofitFactory.setBaseUrl(Urls.ROOT);
2. 与GSON结合
Retrofit2可以很方便的通过Converter与GSON结合, 在RetrofitFactory
类中已经做了绑定, 无需再处理.
builder.addConverterFactory(GsonConverterFactory.create());
我们只需要在定义接口的时候给定结果类型, Retrofit内部会调用GSON解析JSON数据, 并转换成相应的实体类.
Call<BaseData> getTypes();
3. 自定义Log输出
为何要有Log输出? 有了网络请求的Log我们就能很方便的追踪问题, 看是服务器返回的数据有误, 还是我们解析有误. Retrofit2内部使用的是okhttp3, 因此可以考虑使用okhttp的Interceptor来打印Log输出:
/**
* OkHttp的{@link Interceptor}, 通过设置
* {@link Configuration#enableLoggingNetworkParams()}打印网络请求参数与响应结果
*/
public class HttpLoggingInterceptor implements Interceptor {
private static final String TAG = "HttpLogging";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); // 获取请求
long t1 = System.nanoTime();
Buffer buffer = new Buffer();
request.body().writeTo(buffer); //获取请求体
Log.e(TAG, String.format("Sending request %s on %s%n%sRequest Params: %s",
request.url(), chain.connection(), request.headers(), buffer.clone().readUtf8()));
buffer.close();
Response response = chain.proceed(request); //执行请求
long t2 = System.nanoTime();
BufferedSource source = response.body().source(); //获取请求结果
source.request(Long.MAX_VALUE);
buffer = source.buffer().clone(); //克隆返回结果, 因为buffer只能使用一次
Log.e(TAG, String.format("Received response for %s in %.1fms%n%sResponse Json: %s",
response.request().url(), (t2 - t1) / 1e6d, response.headers(),
buffer.readUtf8()));
return response;
}
}
然后在RetrofitFactory
中加入:
if (Configuration.isShowNetworkParams()) {
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(
new HttpLoggingInterceptor()).build();
builder.client(client);
}
以下是Log格式:
HttpLogging: Sending request http://xx.xx.xx.xx:xxxx/xxx/app/findAttractions.htm on null
Request Params: city=%E6%AD%A6%E6%B1%89&pageNo=1&pageSize=10&latitude=xxx&longitude=xxx
HttpLogging: Received response for http://xx.xx.xx.xx:xxxx/xxx/app/findAttractions.htm in 181.2ms
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 23 Mar 2016 06:50:48 GMT
OkHttp-Sent-Millis: 1458715847034
OkHttp-Received-Millis: 1458715847128
Response Json: {"data":{"attractionsList":[{"distance":13110.6,"id":13,"image":"xx.xx.xx.xx:xxxx/xxx/upload/201603171607138491.jpg","latitude":30.559024,"longitude":114.30341,"name":"黄鹤楼","profile":null,"score":5,"voice":"xx.xx.xx.xx:xxxx/xxx/upload/201603171009165109.mp3"}]},"msg":"成功","page":{"pageCount":1,"pageNo":1,"pageSize":10,"totalCount":1},"status":1}
接下来的几部分都建议在有一定RxJava基础的情况下阅读. 参考RxJava.
Last updated
Was this helpful?