okhttp3如今基本都在用的一个底层网络框架。这篇博客主要的目的就是将OKHttp3这个框架在开发中能用到的地方都记录下来,也当一个工具文档为往后使用时查找方便。html
若是已经会了,那么请移步一文了解OKHttp3全(大话原理篇)
前端
首先记得在build.gradle 和 配置文件分别加上依赖和网络权限java
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
implementation 'com.squareup.okio:okio:1.12.0'
复制代码
以及权限android
<uses-permission android:name="android.permission.INTERNET"/>
复制代码
完事了,接下来介绍使用
git
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的扩展属性github
* 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 超时
复制代码
为了测试接下来的功能,咱们本身搭建一个服务器。 先搭建环境,配置tomcat 做者的是mac因此就介绍下mac下配置tomcat的方式,Windows的小伙伴们能够参考这个(www.cnblogs.com/beginner-bo…)apache
打开终端输入命令,简单办法,终端输入cd,而后直接将bin文件夹直接拖拽到终端json
cd /Library/Tomcat/bin跨域
将目标文件受权,终端输入命令浏览器
chmod +x *.sh
启动tomcat
./startup.sh
浏览器中输入:http://localhost:8080/
到这里,恭喜你tomcat配置成功
服务端做者使用的是IDEA,怎么在这里面配置Tomcat,以及后面要使用的Servlet可看这个文章,会用的小伙伴能够跳过(www.cnblogs.com/wfhking/p/9…)
好了,到这里,服务端已经搭建完毕,接下来,咱们书写服务端程序吧!
@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能够正常使用了
在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;
}
}
复制代码
好了,这就能够了
简单起见,咱们直接下载个网上的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权限哈。
为了优化(扯淡的故事)接口要求压缩上传的数据,好了直接贴代码
build.gradle
implementation 'com.zhouyou:rxeasyhttp:2.1.2'
复制代码
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor(new GzipRequestInterceptor())
.build();
复制代码
固然,须要服务器支持,OK,完事了