OkHttp Demo
来源:互联网 发布:小学英语四年级的软件 编辑:程序博客网 时间:2024/06/06 06:38
OkHttp可用来发送http请求,做微服务。使用方法很简单,并没有做很多事情,只需要创建Request(包括url,header,body),创建并执行Call,读取ResponseBody中的数据即可。
1.读取Response数据
1.1.String方式读取
private <T> T callAndReadResponseBodyString(Request request, TypeToken<RemoteApiResult<T>> typeToken){ Response response = null; try { response = this.getOkHttpClient().newCall(request).execute(); if(!response.isSuccessful()) throw new IOException(); } catch (IOException e) { return null; } RemoteApiResult<T> remoteApiResult = this.gson.fromJson(response.body().charStream(), typeToken.getType()); return remoteApiResult.getData(); }
远程服务方将Java对象转成Json格式返回(Spring MVC 的Controller中return的java对象会自动转),调用方可以通过Gson将Json字符串转换为Java对象。
这种方式很方便,但存在一些问题:
1.Java对象(自己定义)属性中不要有byte,byte[]类型,byte[]类型转成Json再转回byte[]不一定对了;
2.官方文档指出当ResponseBody大于1M时推荐以流的方式读取。
1.2.Stream方式读取
private byte[] callAndReadResponseBodyStream(Request request){ Response response = null; try { response = this.getOkHttpClient().newCall(request).execute(); if(!response.isSuccessful()) throw new IOException(); } catch (IOException e) { return new byte[0]; } //获取ResponseBody的输入流 InputStream inputStream = response.body().byteStream(); //从输入流读取字节码 byte[] bytes = StreamUtil.getAllBytesOfInputStream(inputStream); return bytes; }
2.发送Request
2.1.同步Get请求
/** * 同步get请求 * @param url * @return 指定类型的对象 */ public <T> T syncGet(String url, Map<String, String> requestParams, Map<String, String> headers, TypeToken<RemoteApiResult<T>> typeToken){ Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(this.buildHeaders(headers)) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
Controller返回一个Java对象即可。
另外,OkHttp中的url是要加上协议的,如http、https等,否则会报错url无效。
2.2.异步Get请求
/** * 异步get请求 * @param url */ public <T> void asynGet(String url, Map<String, String> requestParams, Map<String, String> headers, ResponseHandler handler){ Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(this.buildHeaders(headers)) .build(); this.getOkHttpClient().newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { if(handler != null) handler.handleFailure(call, e); } @Override public void onResponse(Call call, Response response) throws IOException { if(handler != null) handler.handleResponse(call, response); } }); }
自己实现Callback接口的两个方法即可,这里通过自己定义的ResponseHandler类来处理。
Controller返回void,数据写到HttpServletResponse的输出流里。如:
@RequestMapping(value = "/get/image/author", method = RequestMethod.GET) public void getAuthorImage(HttpServletResponse response){ File file = new File("F:\\demo\\okhttp\\okhttp-service\\src\\main\\resources\\static\\author.jpg"); try(FileInputStream fileInputStream = new FileInputStream(file); OutputStream outputStream = response.getOutputStream() ) { byte[] bytes = new byte[(int) file.length()]; fileInputStream.read(bytes); outputStream.write(bytes); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
2.3.POST String
public <T> T postString(String url, Map<String, String> requestParams, Map<String, String> headers, String str, MediaType mediaType, TypeToken<RemoteApiResult<T>> typeToken){ Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(this.buildHeaders(headers)) .post(RequestBody.create(mediaType, str)) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
RequestBody的数据Controller端有两种方式可以读取:
1.通过HttpServletRequest
@RequestMapping(value = "/reply", method = RequestMethod.POST) public ApiResult<String> sendMessage(HttpServletRequest request){ String msg; try { msg = HttpUtil.readRequestBodyToString(request); } catch (IOException e) { msg = null; } return new ApiResult<>("Your message: \"" + msg + "\" has been received successfully."); }
public static String readRequestBodyToString(HttpServletRequest request) throws IOException { BufferedReader reader = request.getReader(); StringBuilder result = new StringBuilder(); String line; while((line = reader.readLine()) != null){ result.append(line); } return result.toString(); }
2.通过@RequestBody
@RequestMapping(value = "/reply2", method = RequestMethod.POST) public ApiResult<String> sendMessage(@RequestBody String msg){ return new ApiResult<>("Your message: \"" + msg + "\" has been received successfully."); }
2.4.POST Stream
public <T> T postStream(String url, Map<String, String> requestParams, Map<String, String> headers, MediaType mediaType, Object object, TypeToken<RemoteApiResult<T>> typeToken){ RequestBody requestFileBody = new RequestBody() { @Override public MediaType contentType() { return mediaType; } @Override public void writeTo(BufferedSink sink) throws IOException { OutputStream outputStream = sink.outputStream(); String json = gson.toJson(object); byte[] bytes = json.getBytes("utf-8"); outputStream.write(bytes); } }; Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(this.buildHeaders(headers)) .post(requestFileBody) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
感觉就是把Java对象写到流里POST。
Controller可以以byte[]作为参数,数据是可以拿到的,处理方式自己定义即可。
@RequestMapping(value = "/upload", method = RequestMethod.POST) public ApiResult<String> uploadFile(@RequestBody byte[] bytes) { String string = new String(bytes); return new ApiResult<>(string); }
2.5.POST File
public <T> T postFile(String url, Map<String, String> requestParams, Map<String, String> headers, MediaType mediaType, File file, TypeToken<RemoteApiResult<T>> typeToken){ Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url,requestParams)) .headers(this.buildHeaders(headers)) .post(RequestBody.create(mediaType, file)) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
Controller也可以用byte[]作为参数,不过显然不能简单的转成String了,除非是文本文件如txt。
2.6.POST Form表单
public <T> T postFormData(String url, Map<String, String> requestParams, Map<String, String> headers, Map<String, String> formData, TypeToken<RemoteApiResult<T>> typeToken){ Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(buildHeaders(headers)) .post(buildRequestBody(formData)) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
跟在页面提交form表单是一样的效果,所以Controller的写法同页面提交form表单。
2.7.POST Multipart Request
public <T> T postMultipartRequest(String url, Map<String, String> requestParams, Map<String, String> headers, List<FormDataPart> formDataParts, TypeToken<RemoteApiResult<T>> typeToken){ MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); for(FormDataPart part : formDataParts){ if(part instanceof KeyValueFormDataPart) { KeyValueFormDataPart keyValueFormDataPart = (KeyValueFormDataPart) part; builder.addFormDataPart(keyValueFormDataPart.getName(), keyValueFormDataPart.getValue()); } else { FileFormDataPart fileFormDataPart = (FileFormDataPart)part; builder.addFormDataPart(fileFormDataPart.getName(), fileFormDataPart.getFileName(), fileFormDataPart.getRequestBody()); } } RequestBody requestBody = builder.build(); Request request = new Request.Builder() .url(this.buildUrlWithRequestParams(url, requestParams)) .headers(this.buildHeaders(headers)) .post(requestBody) .build(); return this.callAndReadResponseBodyString(request, typeToken); }
这里我自己定义了FormDataPart基类和KeyValueFormDataPart、FileFormDataPart两个子类。
这个可以用于带文件上传的form表单,如:
<form action="..." method="post"> <table> <tr> <td>用户名</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密码</td> <td><input type="password" name="password"/></td> </tr> <tr> <td>头像</td> <td><input type="file" name="icon"/></td> </tr> </table> <input type="submit" value="提交"/></form>
Controller的接收数据方式如下:
@RequestMapping(value = "/update/head", method = RequestMethod.POST) public ApiResult<User> updateHeadIcon(String username, String password, MultipartFile icon){ byte[] bytes; try { bytes = icon.getBytes(); } catch (IOException e) { bytes = null; } return userService.updateHeadIcon(username, password, null); }
file类型的表单项可以用MultipartFile类型作为对应的入参,然后读取字节码即可。
2.8.设置cache、time out,更改OkHttpClient设置
一般,OkHttpClient是事先创建好的单一实例,如:
private HttpUtil(){ client = new OkHttpClient.Builder() .connectTimeout(connectTimeout, TimeUnit.SECONDS) .writeTimeout(writeTimeout, TimeUnit.SECONDS) .readTimeout(readTimeout, TimeUnit.SECONDS) .build(); }
创建单一实例类HttpUtil时对OkHttpClient进行一些配置,如time out。
如果在发送请求之前临时想改变OkHttpClient的一些配置,可以这样做:
public OkHttpClient getOkHttpClient(){ if(cacheable){ return this.client.newBuilder().cache(this.cache).build(); } else { return this.client; } }
通过原来的OkHttpClient得到一个Builder来重新build OkHttpClient。例如,可以临时配置Cache。
private Cache cache = new Cache(new File("F:/demo/okhttp/okhttp-client/src/main/resources/cache"), 10 * 1024 *1024);
注意:不是配置了cache就会生效的,需要在服务方返回的Response头里加上cache相关的配置,如
@RequestMapping(value = "/query", method = RequestMethod.GET) public ApiResult<User> queryUser(HttpServletResponse response, @RequestParam("username") String username){ response.addHeader("Cache-Control", "max-age=3600"); response.addHeader("Last-Modified", Long.toString(System.currentTimeMillis())); return userService.queryUser(username); }
3.其它
问题:Last-Modified没有调试成功,不知道问题出在哪。
本文完整代码:https://github.com/JinchaoLv/okhttp-demo
4.参考
OkHttp官方文档:https://github.com/square/okhttp/wiki/Recipes
- OkHttp Demo
- okhttp请求Demo
- okHttp demo封装
- OKhttp源码解析---demo
- okhttp简单demo
- OkHttp简单封装Demo
- MVP+OKHttp 用户登录demo
- mvp demo:mvp+rxjava+retrofit(okhttp)+greendao
- android上面的OkHttp的简单get和post demo
- 编写天气Demo,接触OKhttp框架,框架没那么难
- okhttp+fastjson+三套环境的android—demo
- Okhttp----缓存的加入方式----附完整demo
- Okhttp----缓存的加入方式----附完整demo
- OkHttp的get post 以及同步请求Demo
- OkHttp
- okhttp
- OkHttp
- OkHttp
- 随笔
- 疯狂Java程序员的基本素养学习笔记
- Cookie详解
- 步进电机定位不准的原因分析
- 框架学习系列 mybatis 第十六篇 mybatis 常用标签三及与hibernate的比较
- OkHttp Demo
- linux命令行下命令参数前的一横(-)和两横(--)的区别
- 两种图像骨架提取算法的研究(1)原理部分
- 1:Two Sum(非最优解)
- Maven项目报错javax.servlet.jsp.JspApplicationContext.getExpressionFactory()Ljavax/el/ExpressionFactory;解
- Golang搭建HTTP服务
- Vue2.0-中篇
- [Kotlin]Kotlin学习笔记(二):Kotlin中流程控制语句的学习(条件、跳转、循环)
- 细数STM32开发板有哪些,官方板/正点原子/野火/安富莱等