1.简介
okhttp3现在基本都在用的一个底层网络框架。这篇博客主要的目的就是将OKHttp3这个框架在开发中能用到的地方都记录下来,也当一个工具文档为日后使用时查找方便。
如果已经会了,那么请移步一文了解OKHttp3全(大话原理篇)
2.环境搭建
首先记得在build.gradle 和 配置文件分别加上依赖和网络权限
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
implementation 'com.squareup.okio:okio:1.12.0'
以及权限
<uses-permission android:name="android.permission.INTERNET"/>
完事了,接下来介绍使用
3.Get使用
OkHttpClient mClient = new OkHttpClient.Builder() // 构建者模式,创建实例
.connectTimeout(20, TimeUnit.SECONDS) // 设置连接超时时间
.build();
Request mRequest = new Request.Builder() // 构建者模式,创建请求信息
.get()
.url("https://www.baidu.com")
.build();
Call call = mClient.newCall(mRequest); // 将request转换成call
call.enqueue(new Callback() { // 执行call
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String strByNet = response.body().string();
// 切换到主线程
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_msg.setText(strByNet);
}
});
}
});
好了,这就是get的简单使用,不过说了是工具,自然要列出扩展 OkHttpClient.Builder的扩展属性
* final Dispatcher dispatcher; //重要:分发器,分发执行和关闭由request构成的Call
* final Proxy proxy; //代理
* final List<Protocol> protocols; //协议
* final List<ConnectionSpec> connectionSpecs; //传输层版本和连接协议
* final List<Interceptor> interceptors; //重要:拦截器
* final List<Interceptor> networkInterceptors; //网络拦截器
* final ProxySelector proxySelector; //代理选择
* final CookieJar cookieJar; //cookie
* final Cache cache; //缓存
* final InternalCache internalCache; //内部缓存
* final SocketFactory socketFactory; //socket 工厂
* final SSLSocketFactory sslSocketFactory; //安全套接层socket 工厂,用于HTTPS
* final CertificateChainCleaner certificateChainCleaner; // 验证确认响应证书 适用 HTTPS 请求连接的主机名。
* final HostnameVerifier hostnameVerifier; // 主机名字确认
* final CertificatePinner certificatePinner; // 证书链
* final Authenticator proxyAuthenticator; //代理身份验证
* final Authenticator authenticator; // 本地身份验证
* final ConnectionPool connectionPool; //连接池,复用连接
* final Dns dns; //域名
* final boolean followSslRedirects; //安全套接层重定向
* final boolean followRedirects; //本地重定向
* final boolean retryOnConnectionFailure; //重试连接失败
* final int connectTimeout; //连接超时
* final int readTimeout; //read 超时
* final int writeTimeout; //write 超时
4.服务器搭建
为了测试接下来的功能,我们自己搭建一个服务器。 先搭建环境,配置tomcat 作者的是mac所以就介绍下mac下配置tomcat的方式,Windows的小伙伴们可以参考这个(www.cnblogs.com/beginner-bo…)
- 下载文件解压
-
打开终端输入命令,简单办法,终端输入cd,然后直接将bin文件夹直接拖拽到终端
-
cd /Library/Tomcat/bin
-
将目标文件授权,终端输入命令
chmod +x *.sh
-
启动tomcat
./startup.sh
-
浏览器中输入:http://localhost:8080/
到这里,恭喜你tomcat配置成功
服务端作者使用的是IDEA,怎么在这里面配置Tomcat,以及后面要使用的Servlet可看这个文章,会用的小伙伴可以跳过(www.cnblogs.com/wfhking/p/9…)
好了,到这里,服务端已经搭建完毕,接下来,我们书写服务端程序吧!
5.post使用
服务端
@WebServlet(name = "TestPost")
public class TestPost extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*"); // 跨域
response.setContentType("text/html"); // 设置响应内容类型
response.setCharacterEncoding("UTF-8"); // 指定编码
// 获取前端传入的数据
BufferedReader br = request.getReader();
String line;
StringBuffer mStringBuff = new StringBuffer();
while ((line = br.readLine()) != null){
mStringBuff.append(line);
}
//设置逻辑实现
PrintWriter out = response.getWriter();
String jsonStr = "服务器收到信息并返回:\n" + mStringBuff.toString();
out.println(jsonStr);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
好了,这样就行了,接下来继续实现我们的客户端的post
客户端
private void doPost(String username, String pass, String hobby) {
// 编码集
final MediaType FORM_CONTENT_TYPE = MediaType.parse("application/json; charset=utf-8");
// 接口地址
final String uri = "http://192.168.0.102:8081/TestOkhttp3_war_exploded/TestPost";
// 创建实例
OkHttpClient okhttp = new OkHttpClient.Builder()
.build();
// 创建表单及数据
HashMap<String, String> map = new HashMap<>();
map.put("username", username);
map.put("password", pass);
map.put("hobby", hobby);
String jsonStr = new Gson().toJson(map);
RequestBody formBody = RequestBody.create(FORM_CONTENT_TYPE, jsonStr);
// 创建请求实例
Request request = new Request.Builder()
.url(uri)
.post(formBody)
.build();
Call call = okhttp.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("lybj", "接口调用失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String strByNet = response.body().string();
// 切换到主线程
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_msg.setText(strByNet);
}
});
}
});
}
当然这么写了之后,会出现一个异常
CLEARTEXT communication ** not permitted by network security policy 这是因为,Android高版本后限制了HTTP访问权限。
解决方案有2个,要么采用https,要么采用下面的方法 在res里面新建xml文件夹,创建文件network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
在application中引用它
<application
...
android:networkSecurityConfig="@xml/network_security_config">
ok了,post可以正常使用了
6.post上传多文件和参数
在project settings -> Artifacts -> 选择自己的工程 -> 右边OutPut directory 中看到自己的输出路径,然后找到该路径,可查看到自己提交的文件及控制台输出的参数信息。
服务端
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
try {
// 设置系统环境
DiskFileItemFactory factory = new DiskFileItemFactory();
// 文件存储的路径
String storePath = getServletContext().getRealPath("/WEB-INF/files");
if(!new File(storePath).exists()){
new File(storePath).mkdirs();
}
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(4 * 1024 * 1024); // 设置单个文件大小不能超过4M
upload.setSizeMax(4 * 1024 * 1024); // 设置总文件上传大小不能超过6M
// 解析
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
// 普通字段,表单提交过来的
if (item.isFormField()){
String name = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(name + "==" + value);
} else { // 解析上传的文件
// String mimeType = item.getContentType(); 获取上传文件类型
// if(mimeType.startsWith("image")){
InputStream in = item.getInputStream();
String fileName = item.getName();
if (fileName == null || "".equals(fileName.trim())) {
continue;
}
fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
fileName = UUID.randomUUID() + "_" + fileName;
// 按日期来建文件夹
String storeFile = storePath + "/" + fileName;
OutputStream out = new FileOutputStream(storeFile);
byte[] b = new byte[1024];
int len = -1;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
in.close();
out.close();
item.delete(); // 删除临时文件
}
}
PrintWriter out = response.getWriter();
out.println("上传成功");
} catch (org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException e) {
pw.write("单个文件不能超过4M");
} catch (org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e) {
pw.write("总文件不能超过6M");
} catch (FileUploadException e) {
e.printStackTrace();
}
}
客户端
/**
* 上传文件
* */
private void doUpload(File file, String userId, String msg){
// 接口地址
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build();
RequestBody fileRequestBody1 = RequestBody.create(MediaTypeUtils.UPLOAD_FILE.value, file);
// 可传多个
MultipartBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("userId", userId)
.addFormDataPart("msg", msg)
.addFormDataPart("file", "myFileName", fileRequestBody1)
.build();
Request rb = new Request.Builder()
.header("Authorization", "Client-ID " + UUID.randomUUID())
.url(ApiUtils.TestPostUpload)
.post(body)
.build();
Call call = client.newCall(rb);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("lybj", "接口调用失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String string = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_msg.setText(string);
}
});
}
});
}
接口封装了下
public interface ApiUtils {
String BaseUri = "http://192.168.0.102:8081/TestOkhttp3_war_exploded/";
// post提交json
String TestPost = BaseUri + "TestPost";
// 上传文件
String TestPostUpload = BaseUri + "TestPostUpload";
}
定义一个枚举类型,用于存储上传使用的信息
public enum MediaTypeUtils {
JSON_UTF_8(MediaType.parse("application/json; charset=utf-8")), // 设置Json数据传输并指定Utf-8为编码集
UPLOAD_FILE(MediaType.parse("multipart/form-data")); // 上传文件
public MediaType value;
MediaTypeUtils(MediaType value) {
this.value = value;
}
}
好了,这就可以了
7.post下载
简单起见,我们直接下载个网上的APK
客户端
/**
* 下载文件
* */
private void doDownload(){
// 接口地址
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.build();
Request rb = new Request.Builder()
.get()
.url(ApiUtils.TestPostDownload)
.build();
Call call = client.newCall(rb);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("lybj", "接口调用失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
writeFile(response);
}
});
}
/**
* 下载文件
* */
private void writeFile(Response response) {
InputStream is = null;
FileOutputStream fos = null;
is = response.body().byteStream();
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(path, "hehe.apk");
try {
fos = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
// 获取下载的文件的大小
long fileSize = response.body().contentLength();
long sum = 0;
int porSize = 0;
while ((len = is.read(bytes)) != -1) {
fos.write(bytes);
sum += len;
porSize = (int) ((sum * 1.0f / fileSize) * 100);
Message message = handler.obtainMessage(1);
message.arg1 = porSize;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("myTag", "下载成功");
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
pro.setProgress(msg.arg1);
}
}
};
好了,到了这里,下载也OK了,上传和下载之前,别忘记动态申请Android权限哈。
8.Gzip压缩
为了优化(扯淡的故事)接口要求压缩上传的数据,好了直接贴代码
build.gradle
implementation 'com.zhouyou:rxeasyhttp:2.1.2'
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor(new GzipRequestInterceptor())
.build();
当然,需要服务器支持,OK,完事了