明天学习了一下
OkHttp
, 在这里做个总结, 心愿能够帮忙到有须要的人, 好了, 废话不多说, 进入正题。
一、OkHttp 介绍
OkHttp 是一个优良的网络申请框架, 可能一说到网络申请框架, 可能很多人都会想到
volley
,volley
是一个 Google 提供的网络申请框架, 我的博客里也有一篇专门介绍volley
的博客, 博客地址在此 **Android 网络申请 —— Volley 的应用 ** 那么既然 Google 提供了网络申请的框架, 咱们为什么还要应用OkHttp
呢, 原来是volley
是要依附HttpCient
的, 而 Google 在Android6.0
的 SDK 中去掉了HttpCient
, 所以OkHttp
就开始越来越受大家的欢送.明天咱们次要介绍
OkHttp
的Get
申请、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);
}
});
}
});
第四步有一些须要留神的中央
- 同步调用会阻塞主线程, 个别不实用
- 异步调用的回调函数是在子线程, 咱们不能在子线程更新 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
对象,FormBody
是RequestBody
的子类, 但有时候咱们经常会遇到要传入一个字符串的需要, 比方客户端给服务器发送一个 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 中, 当然上面是另一种办法 这里咱们应用
BitmapFactory
的decodeStream
将图片的输出流间接转换为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