明天学习了一下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()
中的代码.
@Overridepublic 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的长度
@Overridepublic 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