OKhttp是一个网络请求开源项目,Android网络请求轻量级框架,支持文件上传与下载,支持https。
OKhttp由移动支付Square公司贡献,作为一个建立在开源基础上的公司,在开源方面非常强劲。
在AndroidStudio的gradle中引入
compile 'com.squareup.okhttp3:okhttp:3.9.0'
/** * 同步get请求 */ public void syncGet() { OkHttpClient okHttpClient = new OkHttpClient(); Request requset = new Request.Builder() .url("url?key=valueg&key=value") .build(); final Call call = okHttpClient.newCall(requset); //同步get请求 new Thread(new Runnable() { @Override public void run() { try { Response response = call.execute(); Log.d("test1:", "当前线程名称:" + Thread.currentThread().getName()); Log.d("test1:", "是否响应:" + response.isSuccessful() + ",响应码:" + response.code()); Log.d("test1:", "返回内容:" + response.body().string()); int size = response.headers().size(); for (int i = 0; i < size; i++) { Log.d("test1", response.headers().name(i) + ":" + response.headers().value(i)); } } catch (IOException e) { e.printStackTrace(); } } }).start(); }
以上是OKhttp的get同步请求方式,通过call.execute()方法返回response,这是个阻塞过程,不能直接在主线程直接操作网络等耗时操作,所以开启子线程访问网络。
/** * 异步get请求 */ public void asyncGet() { OkHttpClient okHttpClient = new OkHttpClient(); Request requset = new Request.Builder().url("url?key=value&key=value").build(); final Call call = okHttpClient.newCall(requset); //异步get请求 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.d("test:", "当前线程名称:" + Thread.currentThread().getName()); Log.d("test:", "是否响应:" + response.isSuccessful() + ",响应码:" + response.code()); Log.d("test:", "返回内容:" + response.body().string()); int size = response.headers().size(); for (int i = 0; i < size; i++) { Log.d("test", response.headers().name(i) + ":" + response.headers().value(i)); } } } ); }
以上是OKhttp的get异步请求方式,通过call.enqueue方法后通过Callback的回调返回response,这个非阻塞线程,所以不用我们开启线程,由Okhttp自动开启。
/** * 同步post请求 */ public void syncPost() { OkHttpClient okHttpClient = new OkHttpClient(); FormBody.Builder builder = new FormBody.Builder(); builder.add("key", "value"); builder.add("key", "value"); Request requset = new Request.Builder() .url("url") .post(builder.build()) .build(); final Call call = okHttpClient.newCall(requset); //同步get请求 new Thread(new Runnable() { @Override public void run() { try { Response response = call.execute(); Log.d("test1:", "当前线程名称:" + Thread.currentThread().getName()); Log.d("test1:", "是否响应:" + response.isSuccessful() + ",响应码:" + response.code()); Log.d("test1:", "返回内容:" + response.body().string()); int size = response.headers().size(); for (int i = 0; i < size; i++) { Log.d("test1", response.headers().name(i) + ":" + response.headers().value(i)); } } catch (IOException e) { e.printStackTrace(); } } }).start(); }
通过 FormBody.Builder 健值对表单的形式添加参数,在 Request 的post方法将参数添加到请求体中。然后同步方式获取结果 。
/** * 异步post请求 */ public void asyncPost() { OkHttpClient okHttpClient = new OkHttpClient(); FormBody.Builder builder = new FormBody.Builder(); builder.add("key", "value"); builder.add("key", "value"); Request requset = new Request.Builder() .url("url") .post(builder.build()) .build(); final Call call = okHttpClient.newCall(requset); //异步get请求 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.d("test4:", "当前线程名称:" + Thread.currentThread().getName()); Log.d("test4:", "是否响应:" + response.isSuccessful() + ",响应码:" + response.code()); Log.d("test4:", "返回内容:" + response.body().string()); int size = response.headers().size(); for (int i = 0; i < size; i++) { Log.d("test4", response.headers().name(i) + ":" + response.headers().value(i)); } } } ); }
通过 FormBody.Builder 健值对的形式添加参数,在 Request 的post方法将参数添加到请求体中。然后异步方式获取结果。
以提交json举例
RequestBody jsonBody= RequestBody.create(MediaType.parse("application/json"), "json字符串"); Request requset = new Request.Builder() .url("url") .post(jsonBody) .build(); ...
通过 RequestBody.create指定MediaType类型和json字符串。
10-09 16:09:24.716 22899-22918/? D/test:: 当前线程名称:OkHttp https://way.jd.com/... 10-09 16:09:24.716 22899-22918/? D/test:: 是否响应:true,200 10-09 16:09:24.718 22899-22918/? D/test:: 返回内容:{"code":"10000","charge":false,"msg":"查询成功","result":{"HeWeather5":[{"now":{"hum":"86","vis":"10","pres":"1025","pcpn":"0","fl":"11","tmp":"11","cond":{"txt":"阴","code":"104"},"wind":{"sc":"3-4","spd":"13","deg":"15","dir":"东北风"}},"suggestion":{"uv":{"txt":"属弱紫外线辐射天气,无需特别防护。若长期在户外,建议涂擦SPF在8-12之间的防晒护肤品。","brf":"最弱"},"cw":{"txt":"不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。","brf":"不宜"},"trav":{"txt":"温度适宜,但风稍大,且较强降雨的天气将给您的出行带来很多的不便,若坚持旅行建议带上雨具。","brf":"一般"},"air":{"txt":"气象条件有利于空气污染物稀释、扩散和清除,可在室外正常活动。","brf":"良"},"comf":{"txt":"白天会有降雨,人们会感到有些凉意,但大部分人完全可以接受。","brf":"较舒适"},"drsg":{"txt":"建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。","brf":"较冷"},"sport":{"txt":"有较强降水,建议您选择在室内进行健身休闲运动。","brf":"较不宜"},"flu":{"txt":"天冷空气湿度大,易发生感冒,请注意适当增加衣服,加强自我防护避免感冒。","brf":"易发"}},"aqi":{"city":{"no2":"18","o3":"40","pm25":"5","qlty":"优","so2":"2","aqi":"12","pm10":"5","co":"0"}},"basic":{"city":"北京","update":{"loc":"2017-10-09 17:46","utc":"2017-10-09 09:46"},"lon":"116.40528870","id":"CN101010100","cnty":"中国","lat":"39.90498734"},"daily_forecast":[{"date":"2017-10-09","pop":"96","hum":"73","uv":"1","vis":"12","astro":{"ss":"17:45","mr":"20:27","ms":"09:51","sr":"06:18"},"pres":"1025","pcpn":"36.9","tmp":{"min":"9","max":"15"},"cond":{"txt_n":"中雨","code_n":"306","code_d":"306","txt_d":"中雨"},"wind":{"sc":"微风","spd":"14","deg":"3","dir":"北风"}},{"date":"2017-10-10","pop":"90","hum":"57","uv":"1","vis":"15","astro":{"ss":"17:43","mr":"21:16","ms":"10:59","sr":"06:19"},"pres":"1028","pcpn":"0.0","tmp":{"min":"7","max":"11"},"cond":{"txt_n":"阴","code_n":"104","code_d":"305","txt_d":"小雨"},"wind":{"sc":"微风","spd":"5","deg":"9","dir":"北风"}},{"date":"2017-10-11","pop":"0","hum":"52","uv":"4","vis":"20","astro":{"ss":"17:42","mr":"22:11","ms":"12:04","sr":"06:20"},"pres":"1025","pcpn":"0.0","tmp":{"min":"8","max":"17"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"7","deg":"321","dir":"西北风"}},{"date":"2017-10-12","pop":"2","hum":"55","uv":"4","vis":"20","astro":{"ss":"17:40","mr":"23:12","ms":"13:03","sr":"06:21"},"pres":"1020","pcpn":"0.0","tmp":{"min":"7","max":"15"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"6","deg":"210","dir":"西南风"}},{"date":"2017-10-13","pop":"0","hum":"29","uv":"4","vis":"20","astro":{"ss":"17:39","mr":"09:45","ms":"13:56","sr":"06:22"},"pres":"1022","pcpn":"0.0","tmp":{"min":"10","max":"17"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"4","deg":"175","dir":"南风"}},{"date":"2017-10-14","pop":"13","hum":"49","uv":"3","vis":"19","astro":{"ss":"17:37","mr":"00:15","ms":"14:41","sr":"06:23"},"pres":"1030","pcpn":"13.5","tmp":{"min":"8","max":"14"},"cond":{"txt_n":"阴","code_n":"104","code_d":"104","txt_d":"阴"},"wind":{"sc":"微风","spd":"7","deg":"10","dir":"北风"}},{"date":"2017-10-15","pop":"13","hum":"50","uv":"4","vis":"20","astro":{"ss":"17:36","mr":"01:20","ms":"15:22","sr":"06:24"},"pres":"1034","pcpn":"1.2","tmp":{"min":"9","max":"13"},"cond":{"txt_n":"多云","code_n":"101","code_d":"104","txt_d":"阴"},"wind":{"sc":"微风","spd":"4","deg":"128","dir":"东南风"}}],"hourly_forecast":[{"date":"2017-10-09 19:00","pop":"56","hum":"85","pres":"1028","tmp":"11","cond":{"txt":"中雨","code":"306"},"wind":{"sc":"微风","spd":"12","deg":"12","dir":"东北风"}},{"date":"2017-10-09 22:00","p 10-09 16:09:24.718 22899-22918/? D/test: Server:JDWS/1.0.0 10-09 16:09:24.718 22899-22918/? D/test: Date:Mon, 09 Oct 2017 09:48:55 GMT 10-09 16:09:24.718 22899-22918/? D/test: Content-Type:application/json;charset=utf-8 10-09 16:09:24.718 22899-22918/? D/test: Content-Length:4215 10-09 16:09:24.718 22899-22918/? D/test: Connection:close 10-09 16:09:24.718 22899-22918/? D/test: Expires:Mon, 09 Oct 2017 09:48:55 GMT 10-09 16:09:24.719 22899-22918/? D/test: Cache-Control:max-age=0 10-09 16:09:24.835 22899-22917/? D/test1:: 当前线程名称:Thread-223 10-09 16:09:24.835 22899-22917/? D/test1:: 是否响应:true,200 10-09 16:09:24.836 22899-22917/? D/test1:: 返回内容:{"code":"10000","charge":false,"msg":"查询成功","result":{"HeWeather5":[{"now":{"hum":"86","vis":"10","pres":"1025","pcpn":"0","fl":"11","tmp":"11","cond":{"txt":"阴","code":"104"},"wind":{"sc":"3-4","spd":"13","deg":"15","dir":"东北风"}},"suggestion":{"uv":{"txt":"属弱紫外线辐射天气,无需特别防护。若长期在户外,建议涂擦SPF在8-12之间的防晒护肤品。","brf":"最弱"},"cw":{"txt":"不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。","brf":"不宜"},"trav":{"txt":"温度适宜,但风稍大,且较强降雨的天气将给您的出行带来很多的不便,若坚持旅行建议带上雨具。","brf":"一般"},"air":{"txt":"气象条件有利于空气污染物稀释、扩散和清除,可在室外正常活动。","brf":"良"},"comf":{"txt":"白天会有降雨,人们会感到有些凉意,但大部分人完全可以接受。","brf":"较舒适"},"drsg":{"txt":"建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。","brf":"较冷"},"sport":{"txt":"有较强降水,建议您选择在室内进行健身休闲运动。","brf":"较不宜"},"flu":{"txt":"天冷空气湿度大,易发生感冒,请注意适当增加衣服,加强自我防护避免感冒。","brf":"易发"}},"aqi":{"city":{"no2":"18","o3":"40","pm25":"5","qlty":"优","so2":"2","aqi":"12","pm10":"5","co":"0"}},"basic":{"city":"北京","update":{"loc":"2017-10-09 17:46","utc":"2017-10-09 09:46"},"lon":"116.40528870","id":"CN101010100","cnty":"中国","lat":"39.90498734"},"daily_forecast":[{"date":"2017-10-09","pop":"96","hum":"73","uv":"1","vis":"12","astro":{"ss":"17:45","mr":"20:27","ms":"09:51","sr":"06:18"},"pres":"1025","pcpn":"36.9","tmp":{"min":"9","max":"15"},"cond":{"txt_n":"中雨","code_n":"306","code_d":"306","txt_d":"中雨"},"wind":{"sc":"微风","spd":"14","deg":"3","dir":"北风"}},{"date":"2017-10-10","pop":"90","hum":"57","uv":"1","vis":"15","astro":{"ss":"17:43","mr":"21:16","ms":"10:59","sr":"06:19"},"pres":"1028","pcpn":"0.0","tmp":{"min":"7","max":"11"},"cond":{"txt_n":"阴","code_n":"104","code_d":"305","txt_d":"小雨"},"wind":{"sc":"微风","spd":"5","deg":"9","dir":"北风"}},{"date":"2017-10-11","pop":"0","hum":"52","uv":"4","vis":"20","astro":{"ss":"17:42","mr":"22:11","ms":"12:04","sr":"06:20"},"pres":"1025","pcpn":"0.0","tmp":{"min":"8","max":"17"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"7","deg":"321","dir":"西北风"}},{"date":"2017-10-12","pop":"2","hum":"55","uv":"4","vis":"20","astro":{"ss":"17:40","mr":"23:12","ms":"13:03","sr":"06:21"},"pres":"1020","pcpn":"0.0","tmp":{"min":"7","max":"15"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"6","deg":"210","dir":"西南风"}},{"date":"2017-10-13","pop":"0","hum":"29","uv":"4","vis":"20","astro":{"ss":"17:39","mr":"09:45","ms":"13:56","sr":"06:22"},"pres":"1022","pcpn":"0.0","tmp":{"min":"10","max":"17"},"cond":{"txt_n":"多云","code_n":"101","code_d":"101","txt_d":"多云"},"wind":{"sc":"微风","spd":"4","deg":"175","dir":"南风"}},{"date":"2017-10-14","pop":"13","hum":"49","uv":"3","vis":"19","astro":{"ss":"17:37","mr":"00:15","ms":"14:41","sr":"06:23"},"pres":"1030","pcpn":"13.5","tmp":{"min":"8","max":"14"},"cond":{"txt_n":"阴","code_n":"104","code_d":"104","txt_d":"阴"},"wind":{"sc":"微风","spd":"7","deg":"10","dir":"北风"}},{"date":"2017-10-15","pop":"13","hum":"50","uv":"4","vis":"20","astro":{"ss":"17:36","mr":"01:20","ms":"15:22","sr":"06:24"},"pres":"1034","pcpn":"1.2","tmp":{"min":"9","max":"13"},"cond":{"txt_n":"多云","code_n":"101","code_d":"104","txt_d":"阴"},"wind":{"sc":"微风","spd":"4","deg":"128","dir":"东南风"}}],"hourly_forecast":[{"date":"2017-10-09 19:00","pop":"56","hum":"85","pres":"1028","tmp":"11","cond":{"txt":"中雨","code":"306"},"wind":{"sc":"微风","spd":"12","deg":"12","dir":"东北风"}},{"date":"2017-10-09 22:00"," 10-09 16:09:24.837 22899-22917/? D/test1: Server:JDWS/1.0.0 10-09 16:09:24.837 22899-22917/? D/test1: Date:Mon, 09 Oct 2017 09:48:55 GMT 10-09 16:09:24.837 22899-22917/? D/test1: Content-Type:application/json;charset=utf-8 10-09 16:09:24.837 22899-22917/? D/test1: Content-Length:4215 10-09 16:09:24.837 22899-22917/? D/test1: Connection:close 10-09 16:09:24.837 22899-22917/? D/test1: Expires:Mon, 09 Oct 2017 09:48:55 GMT 10-09 16:09:24.837 22899-22917/? D/test1: Cache-Control:max-age=0
同步调用execute()方法,异步调用enqueue(),
get请求直接URL拼接参数,post需要另外构建参数实体添加到request对象中,
当结果返回时,都不是在主线程,所以不能直接操作UI。这个需要注意!
如果你需要在request的的header添加参数。例如Cookie、Token等
Request requset = new Request.Builder() .url("https://way.jd.com/he/freeweather?city=beijing&appkey=7b84a35601b8715fa49c3714f76844ba") .header("key", "value") .header("key", "value") .build();
能拿到字节流当然我们就能下载文件。
private void uploadAsyncMultiFile() { OkHttpClient okHttpClient = new OkHttpClient(); final String url = "服务器接口地址"; File file = new File("path", "abc.jpg"); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("key1", "fileName", RequestBody.create(MediaType.parse("application/octet-stream"), file)) .addFormDataPart("key2", "value") .build(); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e("test5", "uploadAsyncMultiFile() e=" + e); } @Override public void onResponse(Call call, Response response) throws IOException { Log.i("test5", "uploadAsyncMultiFile() response=" + response.body().string()); } }); }
通过MultipartBody.Builder()上传参数以及文件,
addFormDataPart()随意添加参数和文件对象。
和html提交表单和文件一致效果。
private void downloadFile() { OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder() .url("https://www.baidu.com/") .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e("test6", "downloadFile() e=" + e); } @Override public void onResponse(Call call, Response response) throws IOException { InputStream inputStream = response.body().byteStream(); byte[] b = new byte[1024]; File outFile = new File("/sdcard/baidu.html"); if (!outFile.exists()){ outFile.createNewFile(); }else { outFile.delete(); } FileOutputStream fileOutputStream = new FileOutputStream(outFile); BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream); int len; while ((len=inputStream.read(b))!=-1){ bos.write(b,0,len); } bos.flush(); bos.close(); inputStream.close(); Log.i("test6", "uploadAsyncMultiFile() 下载完成"); } }); }
既然能拿到输入流response.body().byteStream() 那么写文件到本地也就是很简单的事情了。