聊聊 Android 的网络请求框架 Retrofit 2 + okhttp 3
工作中跟客户端沟通时产生了点问题, 记录一下.
起因
后端有一个接口是这样的
@RequestMapping("android") public BaseResult android(@RequestBody RequestData requestData) { }
RequestData.java
package com.junbaor.network.model; public class RequestData { private Integer id; private String name; // 省略 get set }
如果使用 http://localhost:8080/android?id=1&name=张三
是调不通的
只能处理 Content-Type 为 application/json 的请求, 需要把参数放在 post 请求体内
{ "id":1, "name":"张三" }
问题
客户端使用第一种方式调不通, 告知需把参数转成 json 对象放到请求体内,
反馈说之前都是按照第一种方式调用的接口, 做不到第二种。
解决
后端没有找到优雅的解决方式, 被迫修改接口实现, 之所以继续保留 requestData 是为了兼容其它调用方.
@RequestMapping(value = "android") public BaseResult android(@RequestBody(required = false) RequestData requestData, @RequestParam(required = false) Integer id, @RequestParam(required = false) String name) { // 处理逻辑时先判断 requestParam 是否有值, 没有的话再从 requestData 取 }
研究 Android
long long ago 写过一点 Android , 不太相信想自己试试。
打听后得知客户端的网络框架是 Retrofit 配合 OkHttp。
开始研究 Android, 安装环境就不说了。
引包
先引入客户端使用的网络框架, 使用的都是最新版, 由于数据是 json 格式再引入 gson 库(虽然不知道他们用的是什么解析库). converter-gson 是 retrofit2 的工具包, 用来把 json 封装成对象.
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.okhttp3:okhttp:3.9.1' compile 'com.google.code.gson:gson:2.8.2' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
编码
// OkHttp 请求日志拦截器 HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { System.out.println(message); } }); logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // OkHttp 客户端 OkHttpClient okHttpClient = new OkHttpClient().newBuilder() .connectTimeout(2, TimeUnit.MILLISECONDS) .addNetworkInterceptor(logInterceptor) .build(); // 把后台返回的时间戳转成 java.util.Date Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new GsonDateTypeAdapter()).create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://127.0.0.1:8080") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //请求参数 RequestData requestData = new RequestData(); requestData.setId(1); requestData.setName("张三"); TestService testService = retrofit.create(TestService.class); Call testServiceReslut = testService.getTestResult(requestData); testServiceReslut.enqueue(new Callback<ResponseData>() { @Override public void onResponse(Call<ResponseData> call, Response<ResponseData> response) { System.out.println("响应成功, 数据:" + response.body()); } @Override public void onFailure(Call<ResponseData> call, Throwable t) { System.out.println("响应失败, 原因:" + t.getMessage()); } });
源码
参见:https://github.com/junbaor/an...
Android 端重点关注:com.junbaor.network.NetworkTest
Server 端重点关注:com.junbaor.network.NetworkApplication
花絮
Gson 时间戳转 Date
GsonDateTypeAdapter.java
package com.junbaor.network.extend; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.Date; /** * Created by junbaor on 2017/12/13. * * 将后台返回的时间戳转成 java.util.Date * 参见: https://stackoverflow.com/questions/41348055/gson-dateformat-to-parse-output-unix-timestamps */ public class GsonDateTypeAdapter extends TypeAdapter<Date> { @Override public void write(JsonWriter out, Date value) throws IOException { if (value == null) { out.nullValue(); } else { out.value(value.getTime() / 1000); } } @Override public Date read(JsonReader in) throws IOException { if (in != null) { return new Date(in.nextLong() / 1000); } else { return null; } } }
okhttp 日志打印
在网上找到了这个 https://github.com/square/okh...
由于没有 logger 实现, 打印的比较凌乱, 索性把代码拷到项目中以便修改, 用控制台输出实现了 logger 接口
// OkHttp 日志拦截器 HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { System.out.println(message); } }); logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
日志效果:
--> POST http://127.0.0.1:8080/android http/1.1 Content-Type: application/json; charset=UTF-8 Content-Length: 24 Host: 127.0.0.1:8080 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/3.9.1 {"id":1,"name":"张三"} --> END POST (24-byte body) <-- 200 http://127.0.0.1:8080/android (15ms) Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Wed, 13 Dec 2017 16:05:49 GMT {"code":200,"message":"成功","data":{"id":1,"name":"张三","birthday":1513181149729}} <-- END HTTP (88-byte body) 响应成功, 数据:BaseResult{code=200, message='成功', data=ResponseData{id=1, name='张三', birthday=Sun Jan 18 20:19:41 CST 1970}}