关于android:OkHttp使用详解

4次阅读

共计 9119 个字符,预计需要花费 23 分钟才能阅读完成。

明天学习了一下OkHttp, 在这里做个总结, 心愿能够帮忙到有须要的人, 好了, 废话不多说, 进入正题。

一、OkHttp 介绍

OkHttp 是一个优良的网络申请框架, 可能一说到网络申请框架, 可能很多人都会想到 volley,volley 是一个 Google 提供的网络申请框架, 我的博客里也有一篇专门介绍 volley 的博客, 博客地址在此 **Android 网络申请 —— Volley 的应用 ** 那么既然 Google 提供了网络申请的框架, 咱们为什么还要应用 OkHttp 呢, 原来是 volley 是要依附 HttpCient 的, 而 Google 在 Android6.0 的 SDK 中去掉了 HttpCient, 所以OkHttp 就开始越来越受大家的欢送.

明天咱们次要介绍 OkHttpGet申请、Post申请、上传下载文件 上传下载图片等性能

_当然在开始之前, 咱们还要先在我的项目中增加 OkHttp 的依赖库, 至于怎么在 AndroidStudio 中给我的项目增加 OkHTTP 依赖, 这里将不再赘述。另外,OkHttp 中应用了建造者模式, 如果对建造者模式不理解, 能够看看这篇博客 设计模式之建造者模式_**

增加 OkHttp 的依赖

在对应的 Module 的 gradle 中增加
compile 'com.squareup.okhttp3:okhttp:3.5.0'   
而后同步一下我的项目即可

二、OkHttp 进行 Get 申请

应用 OkHttp 进行 Get 申请只须要四步即可实现。

1 . 拿到 OkHttpClient 对象

OkHttpClient client = new OkHttpClient();

2 . 结构 Request 对象

Request request = new Request.Builder()
                .get()
                .url("https:www.baidu.com")
                .build();

这里咱们采纳建造者模式和链式调用指明是进行 Get 申请, 并传入 Get 申请的地址

如果咱们须要在 get 申请时传递参数, 咱们能够以上面的形式将参数拼接在 url 之后

https:www.baidu.com?username=admin&password=admin

3 . 将 Request 封装为 Call

Call call = client.newCall(request);

4 . 依据须要调用同步或者异步申请办法

// 同步调用, 返回 Response, 会抛出 IO 异样
Response response = call.execute();

// 异步调用, 并设置回调函数
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {Toast.makeText(OkHttpActivity.this, "get failed", Toast.LENGTH_SHORT).show();}

    @Override
    public void onResponse(Call call, final Response response) throws IOException {final String res = response.body().string();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {contentTv.setText(res);
            }
        });
    }
});

第四步有一些须要留神的中央

  1. 同步调用会阻塞主线程, 个别不实用
  2. 异步调用的回调函数是在子线程, 咱们不能在子线程更新 UI, 须要借助于 runOnUiThread() 办法或者 Handler 来解决

是不是认为下面就完结了, 对的,OkHttp 的 Get 申请步骤就这么 4 步, 然而当你试图关上利用加载数据, 可是发现并没有加载到数据, 这是一个简略然而咱们常犯的谬误. 在 AndroidManifest.xml 中退出联网权限

<uses-permission android:name="android.permission.INTERNET" />

三、OkHttp 进行 Post 申请提交键值对

应用 OkHttp 进行 Post 申请和进行 Get 申请很相似, 只须要五步即可实现。

1 . 拿到 OkHttpClient 对象

OkHttpClient client = new OkHttpClient();

2 . 构建 FormBody, 传入参数

FormBody formBody = new FormBody.Builder()
                .add("username", "admin")
                .add("password", "admin")
                .build();

3 . 构建 Request, 将 FormBody 作为 Post 办法的参数传入

final Request request = new Request.Builder()
                .url("http://www.jianshu.com/")
                .post(formBody)
                .build();

4 . 将 Request 封装为 Call

Call call = client.newCall(request);

5 . 调用申请, 重写回调办法

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {Toast.makeText(OkHttpActivity.this, "Post Failed", Toast.LENGTH_SHORT).show();}

    @Override
    public void onResponse(Call call, Response response) throws IOException {final String res = response.body().string();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {contentTv.setText(res);
            }
        });
    }
});

通过下面的步骤一个 post 申请就实现了, 当然下面的 url 参数和须要传入的参数大家就要依据理论状况来传入, 你会发现 get 和 post 申请的步骤十分像。

四、OkHttp 进行 Post 申请提交字符串

如果你曾经把握了下面的两种根本的步骤, 那上面的内容就比较简单了

下面咱们的 post 的参数是通过结构一个 FormBody 通过键值对的形式来增加进去的, 其实 post 办法须要传入的是一个 RequestBody 对象,FormBodyRequestBody 的子类, 但有时候咱们经常会遇到要传入一个字符串的需要, 比方客户端给服务器发送一个 json 字符串, 那这种时候就须要用到另一种形式来结构一个 RequestBody 如下所示:

RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:admin;password:admin}");

下面的 MediaType 咱们指定传输的是纯文本, 而且编码方式是 utf-8, 通过下面的形式咱们就能够向服务端发送 json 字符串啦。

注: 对于 MidiaType 的类型你能够百度搜寻 mime type 查看相干的内容, 这里不再赘述

五、OkHttp 进行 Post 申请上传文件

了解了下面一个, 上面这个就更简略了, 这里咱们以上传一张图片为例, 当然你也能够上传一个 txt 什么的文件, 都是能够的

其实最次要的还是构架咱们本人的RequestBody, 如下图构建

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show();}else{RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
}

这里咱们将手机 SD 卡根目录下的 1.png 图片进行上传。代码中的 application/octet-stream 示意咱们的文件是 任意二进制数据流, 当然你也能够换成更具体的image/png

注: 最初记得最重要的一点: 增加存储卡写权限, 在 AndroidManifest.xml 文件中增加如下代码:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

六、OkHttp 进行 Post 申请提交表单

咱们在网页上常常会遇到用户注册的状况, 须要你输出用户名, 明码, 还有上传头像, 这其实就是一个表单, 那么接下来咱们看看如何利用 OkHttp 来进行表单提交。通过下面的学习, 大家必定也懂, 次要的区别就在于结构不同的 RequestBody 传递给 post 办法即可.

因为咱们应用的是 OkHttp3 所以咱们还须要再导入一个包 okio.jar 能力持续上面的内容, 咱们须要在模块的 Gradle 文件中增加如下代码, 而后同步一下我的项目即可

compile 'com.squareup.okio:okio:1.11.0'

这里咱们会用到一个 MuiltipartBody, 这是RequestBody 的一个子类, 咱们提交表单就是利用这个类来构建一个RequestBody, 上面的代码咱们会发送一个蕴含用户民、明码、头像的表单到服务端

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show();
    return;
}
RequestBody muiltipartBody = new MultipartBody.Builder()
        // 肯定要设置这句
        .setType(MultipartBody.FORM)
        .addFormDataPart("username", "admin")//
        .addFormDataPart("password", "admin")//
        .addFormDataPart("myfile", "1.png", RequestBody.create(MediaType.parse("application/octet-stream"), file))
        .build();

下面增加用户民和明码的局部和咱们下面学习的提交键值对的办法很像, 咱们要害要留神以下几点:

(1)如果提交的是表单, 肯定要设置 setType(MultipartBody.FORM) 这一句

(2)提交的文件 addFormDataPart() 的第一个参数, 就下面代码中的 myfile 就是相似于键值对的键, 是供服务端应用的, 就相似于网页表单外面的 name 属性, 例如上面:

<input type="file" name="myfile">

(3)提交的文件 addFormDataPart() 的第二个参数文件的本地的名字, 第三个参数是RequestBody, 外面蕴含了咱们要上传的文件的门路以及MidiaType

(4)记得在 AndroidManifest.xml 文件中增加存储卡读写权限

七、OkHttp 进行 get 申请下载文件

除了下面的性能, 咱们最罕用的性能该有从网路上下载文件, 咱们上面的例子将演示下载一个文件寄存在存储卡根目录, 从网络下载一张图片并显示到 ImageView 中

1 . 从网络下载一个文件(此处咱们以下载一张图片为例)

public void downloadImg(View view){OkHttpClient client = new OkHttpClient();
    final Request request = new Request.Builder()
            .get()
            .url("https://www.baidu.com/img/bd_logo1.png")
            .build();
    Call call = client.newCall(request);
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {Log.e("moer", "onFailure:");;
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            // 拿到字节流
            InputStream is = response.body().byteStream();

            int len = 0;
            File file  = new File(Environment.getExternalStorageDirectory(), "n.png");
            FileOutputStream fos = new FileOutputStream(file);
            byte[] buf = new byte[128];

            while ((len = is.read(buf)) != -1){fos.write(buf, 0, len);
            }

            fos.flush();
            // 敞开流
            fos.close();
            is.close();}
    });
}

你会发现步骤与进行个别的 Get 申请差异不大, 惟一的区别在于咱们在回调函数中所做的事, 咱们拿到了图片的字节流, 而后保留为了本地的一张图片

2 . 从网络下载一张图片并设置到 ImageView 中

其实学会了下面的步骤你齐全能够将图片下载到本地后再设置到 ImageView 中, 当然上面是另一种办法 这里咱们应用 BitmapFactorydecodeStream将图片的输出流间接转换为 Bitmap, 而后设置到ImageView 中, 上面只给出 onResponse() 中的代码.

@Override
public void onResponse(Call call, Response response) throws IOException {InputStream is = response.body().byteStream();

    final Bitmap bitmap = BitmapFactory.decodeStream(is);
    runOnUiThread(new Runnable() {
        @Override
        public void run() {imageView.setImageBitmap(bitmap);
        }
    });

    is.close();}

八、给文件的上传和下载加上进度条

咱们始终都说, 用户体验很重要, 当咱们下载的文件比拟大, 而网速又比较慢的时候, 如果咱们只是在后盾下载或上传, 没有给用户显示一个进度, 那将是十分差的用户体验, 上面咱们就将简略做一下进度的显示, 其实非常简单的

1 . 显示文件下载进度

这里只是演示, 我只是把进度显示在一个 TextView 中, 至于进度的获取当然是在咱们的回调函数 onResponse() 中去获取

(1)应用 response.body().contentLength() 拿到文件总大小

(2)在 while 循环中每次递增咱们读取的 buf 的长度

@Override
public void onResponse(Call call, Response response) throws IOException {InputStream is = response.body().byteStream();
    long sum = 0L;
    // 文件总大小
    final long total = response.body().contentLength();
    int len = 0;
    File file  = new File(Environment.getExternalStorageDirectory(), "n.png");
    FileOutputStream fos = new FileOutputStream(file);
    byte[] buf = new byte[128];

    while ((len = is.read(buf)) != -1){fos.write(buf, 0, len);
        // 每次递增
        sum += len;

        final long finalSum = sum;
        Log.d("pyh1", "onResponse:" + finalSum + "/" + total);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 将进度设置到 TextView 中
                contentTv.setText(finalSum + "/" + total);
            }
        });
    }
    fos.flush();
    fos.close();
    is.close();}

2 . 显示文件上传进度

对于上传的进度的解决会比拟麻烦, 因为具体的上传过程是在 RequestBody 中由 OkHttp 帮咱们解决上传, 而且 OkHttp 并没有给咱们提供上传进度的接口, 这里咱们的做法是自定义类继承RequestBody, 而后重写其中的办法, 将其中的上传进度通过接口回调裸露进去供咱们应用。

public class CountingRequestBody extends RequestBody {
    // 理论起作用的 RequestBody
    private RequestBody delegate;
    // 回调监听
    private Listener listener;

    private CountingSink countingSink;

    /**
     * 构造函数初始化成员变量
     * @param delegate
     * @param listener
     */
    public CountingRequestBody(RequestBody delegate, Listener listener){
        this.delegate = delegate;
        this.listener = listener;
    }
    @Override
    public MediaType contentType() {return delegate.contentType();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {countingSink = new CountingSink(sink);
        // 将 CountingSink 转化为 BufferedSink 供 writeTo()应用
        BufferedSink bufferedSink = Okio.buffer(countingSink);
        delegate.writeTo(bufferedSink);
        bufferedSink.flush();}

    protected final class CountingSink extends ForwardingSink{
        private long byteWritten;
        public CountingSink(Sink delegate) {super(delegate);
        }

        /**
         * 上传时调用该办法, 在其中调用回调函数将上传进度裸露进来, 该办法提供了缓冲区的本人大小
         * @param source
         * @param byteCount
         * @throws IOException
         */
        @Override
        public void write(Buffer source, long byteCount) throws IOException {super.write(source, byteCount);
            byteWritten += byteCount;
            listener.onRequestProgress(byteWritten, contentLength());
        }
    }

    /**
     * 返回文件总的字节大小
     * 如果文件大小获取失败则返回 -1
     * @return
     */
    @Override
    public long contentLength(){
        try {return delegate.contentLength();
        } catch (IOException e) {return -1;}
    }

    /**
     * 回调监听接口
     */
    public static interface Listener{
        /**
         * 暴露出上传进度
         * @param byteWritted  曾经上传的字节大小
         * @param contentLength 文件的总字节大小
         */
        void onRequestProgress(long byteWritted, long contentLength);
    }
}

下面的代码正文十分具体, 这里不再解释, 而后咱们在写具体的申请时还须要做如下变动

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show();}else{RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
}

// 应用咱们本人封装的类
CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody2, new CountingRequestBody.Listener() {
    @Override
    public void onRequestProgress(long byteWritted, long contentLength) {
        // 打印进度
        Log.d("pyh", "进度:" + byteWritted + "/" + contentLength);
    }
});

下面其实就是在原有的 RequestBody 上包装了一层, 最初在咱们的应用中在 post() 办法中传入咱们的 CountingRequestBody 对象即可。

九、后记

以上就是一些 OkHttp 罕用的总结, 心愿能够帮忙到须要的人

本文转自 https://juejin.cn/post/6844903527655931911,如有侵权,请分割删除。

相干视频举荐:

【2021 最新版】Android studio 装置教程 +Android(安卓)零基础教程视频(适宜 Android 0 根底,Android 初学入门)含音视频_哔哩哔哩_bilibili

【Android 进阶教程】——OkHttp 原理_哔哩哔哩_bilibili

正文完
 0