一文了解OKHttp3全(使用篇)

1.簡介

okhttp3如今基本都在用的一個底層網絡框架。這篇博客主要的目的就是將OKHttp3這個框架在開發中能用到的地方都記錄下來,也當一個工具文檔爲往後使用時查找方便。html

若是已經會了,那麼請移步一文了解OKHttp3全(大話原理篇)
前端

2.環境搭建

首先記得在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

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的擴展屬性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 超時
複製代碼

4.服務器搭建

爲了測試接下來的功能,咱們本身搭建一個服務器。 先搭建環境,配置tomcat 做者的是mac因此就介紹下mac下配置tomcat的方式,Windows的小夥伴們能夠參考這個(www.cnblogs.com/beginner-bo…)apache

  1. 下載Tomcat
  1. 下載文件解壓
  1. 打開終端輸入命令,簡單辦法,終端輸入cd,而後直接將bin文件夾直接拖拽到終端json

  2. cd /Library/Tomcat/bin跨域

  3. 將目標文件受權,終端輸入命令瀏覽器

    chmod +x *.sh

  4. 啓動tomcat

    ./startup.sh

  5. 瀏覽器中輸入: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,完事了

9.源碼下載

客戶端的源碼

服務端的源碼

相關文章
相關標籤/搜索