安卓网络编程学习(2)——使用OkHttp框架

写在前面

本文承接https://www.cnblogs.com/wushenjiang/p/12937857.html,接着上文继续学习。
前几篇博客我们主要学习了使用java原生网络编程来进行一些经典的操作。现在我们使用OKhttp将之前的操作再重新操作一遍。

OkHttp优点

  • 1.请求同一主机的时候,一般是指同一域名,支持共享同一个socket.
  • 2.通过连接池减少请求延时
  • 3.传输通过GZIP压缩,减少下载内容的体积
  • 4.用缓存的方式避免重复的请求

可以看到,OkHttp其实是对原生http请求的一个简单封装,并做了额外的一些工作让我们的请求访问更加简单通畅。

OkHttp入门

首先要导入依赖,我们可以使用AndroidStudio或者去github搜索然后copy依赖地址都可以。
首先我们就来用OkHttp来进行一个最基本的异步get请求:

 public void getRequest(View v) {
        //要有客户端,类似我们要有一个浏览器
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
        //创建请求内容
        Request request = new Request.Builder()
                .get()
                .url(BASE_URL + "/get/text")
                .build();
        //用client去创建请求任务
        Call task = okHttpClient.newCall(request);
        //异步请求
        task.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.d(TAG, "onFailure... -->" + e);
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                int code = response.code();
                if (code == HttpURLConnection.HTTP_OK) {
                    Log.d(TAG, "code -->" + code);
                    ResponseBody body = response.body();
                    Log.d(TAG, "body -->" + body.string());
                }
            }
        });

    }

可以看到,使用OkHttp的封装使得我们相比最基础的java原生网络编程要方便的多,我们只需要按照步骤来就可以很简单的使用网络请求了。当然我们还需要设置网络权限,详情可以看上上篇博客。

OkHttp的异步Post请求

上面学习了异步Get请求,那我们再学习一下异步Post请求吧:

 //先有client
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
        //要提交的内容
        CommentItem commentItem = new CommentItem("234134123", "我是评论内容...哈哈");
        Gson gson = new Gson();
        String jsonStr = gson.toJson(commentItem);
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody requestBody = RequestBody.create(jsonStr, mediaType);

        Request request = new Request.Builder()
                .post(requestBody)
                .url(BASE_URL + "/post/comment")
                .build();
        //用client去创建任务
        Call task = okHttpClient.newCall(request);
        //异步请求
        task.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.d(TAG, "onFailure -->" + e.toString());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                int code = response.code();
                Log.d(TAG, "code -->" + code);
                if (code == HttpURLConnection.HTTP_OK) {
                    ResponseBody body = response.body();
                    if (body != null) {
                        Log.d(TAG, "result -->" + body.string());
                    }
                }
            }
        });

比起Get请求,我们多了封装请求内容的部分代码。仔细观察我们可以看到,这部分代码很简单:首先我们new一个请求对象出来,然后通过Gson将其转换成json类型字符串,并标志为json类型即可放入请求头。其余的部分我们就发现基本是一致的。
至于带参数的get和post请求,我们只需要在url里拼接字符串即可,这里不再过多的叙述了。

Post方式文件上传

我们先来贴代码:

public void postFile(View v) {
        //先有client
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
        File file = new File("/storage/emulated/0/Android/data/com.androidlearing.androidnetworkdemo/files/Pictures10.png");
        MediaType fileType = MediaType.parse("image/png");
        RequestBody fileBody = RequestBody.create(file, fileType);
        RequestBody requestBody = new MultipartBody.Builder()
                .addFormDataPart("file", file.getName(), fileBody)
                .build();
        //创建请求内容
        Request request = new Request.Builder()
                .url(BASE_URL + "/file/upload")
                .post(requestBody)
                .build();
        //创建请求任务
        Call task = okHttpClient.newCall(request);
        //异步执行
        task.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.d(TAG, "onFailure -->" + e.toString());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                int code = response.code();
                Log.d(TAG, "code ==>" + code);
                if (code == HttpURLConnection.HTTP_OK) {
                    ResponseBody body = response.body();
                    if (body != null) {
                        String string = body.string();
                        Log.d(TAG, "result -->" + string);
                    }
                }
            }
        });
    }

可以看到,我们的代码和上面的post请求并无太大差别,同样需要设置一个请求头。但这里我们由于是文件,就用到了RequestBody的实现类MultipartBody建造者模式下的addFormDataPart方法即可。
具体也很简单,构造一下即可。

多文件下载

上面我们注意到是addFormDataPart方法,一般add都是可以添加很多内容的,我们点进去方法看看注解:

我们可以看到,这里明显是把Part类进行了foreach循环,然后拼接起来了。说明我们的猜想没有错。我们可以直接在拼接请求头时添加多个文件:

public void postFiles(View v) {
        //先有client
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
        File fileOne = new File("/storage/emulated/0/Android/data/com.androidlearing.androidnetworkdemo/files/Pictures10.png");
        File fileTwo = new File("/storage/emulated/0/Download/rBsADV64HDWAI6i_AAhJfxL8eXE287.png");
        File fileThree = new File("/storage/emulated/0/Download/rBsADV64ILeAfwQMAAdBpy-0H04021.png");
        File fileFour = new File("/storage/emulated/0/Download/shop-ad.png");
        MediaType fileType = MediaType.parse("image/png");
        RequestBody fileOneBody = RequestBody.create(fileOne, fileType);
        RequestBody fileTwoBody = RequestBody.create(fileTwo, fileType);
        RequestBody fileThreeBody = RequestBody.create(fileThree, fileType);
        RequestBody fileFourBody = RequestBody.create(fileFour, fileType);
        RequestBody requestBody = new MultipartBody.Builder()
                .addFormDataPart("files", fileOne.getName(), fileOneBody)
                .addFormDataPart("files", fileTwo.getName(), fileTwoBody)
                .addFormDataPart("files", fileThree.getName(), fileThreeBody)
                .addFormDataPart("files", fileFour.getName(), fileFourBody)
                .build();
        //创建请求内容
        Request request = new Request.Builder()
                .url(BASE_URL + "/files/upload")
                .post(requestBody)
                .build();
        //创建请求任务
        Call task = okHttpClient.newCall(request);
        //异步执行
        task.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.d(TAG, "onFailure -->" + e.toString());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                int code = response.code();
                Log.d(TAG, "code ==>" + code);
                if (code == HttpURLConnection.HTTP_OK) {
                    ResponseBody body = response.body();
                    if (body != null) {
                        String string = body.string();
                        Log.d(TAG, "result -->" + string);
                    }
                }
            }
        });
    }

可以看到除了拼接头部部分其余部分基本一模一样。
这里拼接请求头,和上面基本一样,改个名字即可。

下载文件

我们先看核心部分:

    private void downloadFile(InputStream inputStream, Headers headers) throws IOException {
        for (int i = 0; i < headers.size(); i++) {
            String key = headers.name(i);
            String value = headers.value(i);
            Log.d(TAG, key + " ==" + value);
        }
        String contentType = headers.get("Content-disposition");
        String fileName = contentType.replace("attachment; filename=", "");
        File outFile = new File(OkhttpActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + File.separator + fileName);
        if (!outFile.getParentFile().exists()) {
            outFile.mkdirs();
        }
        if (!outFile.exists()) {
            outFile.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(outFile);
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
            fos.write(buffer, 0, len);
        }
        fos.flush();
        IOUtils.ioClose(fos);
        IOUtils.ioClose(inputStream);
    }

可以看到,下载文件其实就是获取filename和路径并且将二进制文件进行解析写入的过程。整体其实和我们在java原生网络编程的使用并无太大差别。
下面是全部文件下载的代码:

 public void downFile(View v) {
        //先有client
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .build();
        Request request = new Request.Builder()
                .get()
                .url(BASE_URL + "/download/15")
                .build();
        final Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.d(TAG, "onFailure -->" + e.toString());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                int code = response.code();
                Log.d(TAG, "code -->" + code);
                if (code == HttpURLConnection.HTTP_OK) {
                    downloadFile(response.body().byteStream(),response.headers());
                }
            }
        });

总结

可以看到,使用OkHttp极大的减少了我们的重复代码,但可以看到封装的还是不够彻底。接下来我们学习一个二次封装的框架:Retrofit.

原文地址:https://www.cnblogs.com/wushenjiang/p/12944345.html