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,完事了