OkHttp基本使用
简介
OkHttp是一个高效的HTTP客户端,它有以下默认特性:
支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
连接池减少请求延时
透明的GZIP压缩减少响应数据的大小
缓存响应内容,避免一些完全重复的请求
当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。
note: OkHttp 支持 Android 2.3 及以上版本Android平台, 对应于 Java, JDK 1.7及以上.
二.使用
OkHttp的使用是非常简单的. 它的请求/响应 API 使用构造器模式builders来设计,它支持阻塞式的同步请求和带回调的异步请求。
官网:https://github.com/square/okhttp
添加依赖:implementation 'com.squareup.okhttp3:okhttp:3.11.0'
讲解案例,前四个,案例图:
GET异步请求
-导入依赖
-new OkHttpClient;
-构造Request对象;
-通过前两步中的对象构建Call对象;
-通过Call#enqueue(Callback)方法来提交异步请求;
注意:1.onResponse属于次线程,不能更新UI主线程中的组件,需要handler
2.response.body().string() 只能执行一次,原因:把inputStream数据流打开读取完数据后,会close关闭流
``
String url = “http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/3”;
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override //失败的方法
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override //成功的方法,得到响应的json数据
public void onResponse(Call call, Response response) throws IOException {
String json = response.body().string(); //此方法只能调用一次,因为会关闭数据流
Log.d(TAG, "onResponse: " + json);
//注意:此方法属于次线程,如果更新UI组件,需要Handler等处理
//后续可以进行:json解析,展示数据在RecyclerView上
}
});
异步发起的请求会被加入到 Dispatcher 中的 runningAsyncCalls双端队列中通过线程池来执行。
GET同步请求
前面几个步骤和异步方式一样,只是最后一部是通过 Call#execute() 来提交请求,注意这种方式会阻塞调用线程,所以在Android中应放在子线程中执行,否则有可能引起ANR异常,Android3.0 以后已经不允许在主线程访问网络。
String url = "http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/3";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = call.execute();
Log.d(TAG, "run: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}}).start();
POST方式异步提交String
这种方式与前面的区别就是在构造Request对象时,需要多构造一个RequestBody对象,用它来携带我们要提交的数据。在构造 RequestBody 需要指定MediaType,用于描述请求/响应 body 的内容类型,关于 MediaType 的更多信息可以查看 https://tools.ietf.org/html/rfc2045,RequstBody的几种构造方式:
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
RequestBody requestBody = RequestBody.create(mediaType, "i am jerry");//创建请求的体:数据
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, response.protocol() + " " +response.code() + " " + response.message());
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ":" + headers.value(i));
}
Log.d(TAG, "onResponse: " + response.body().string());
}
});
POST方式同步提交String:同步部分和GET请求的同步写法一致
OKHttp结合项目案例:创建一个新的model
技术点:
1.OKHttp网络请求 2.下拉刷新上拉加载 3.缓存 4.存储的读写权限的动态申请
接口:http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/3
2.6SmartRefreshLayout实现下拉刷新,上拉加载
依赖:
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.4-7'
implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.0.4-7'
在界面中引入SmartRefreshLayout,包裹 列表组件:RecyclerView或ListView等
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/smartRe" >
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/myrec" />
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
添加OnRefreshListener监听,实现下来刷新,OnLoadMoreListener监听,实现上拉加载
//添加下拉刷新监听器
mSmartRe.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
//下拉刷新
}
});
//添加上拉加载监听器
mSmartRe.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
//上拉加载
}
});
OKHttp缓存,自带缓存功能,默认不添加缓存,添加缓存,需要自行添加:
long maxSize = 1024*1024*100; //100m大小
//手动指定缓存路径
Cache cache = new Cache(new File("/storage/emulated/0/day1_cache"), maxSize);
//使用系统指定的缓存路径
// Cache cache1 = new Cache(getExternalCacheDir(), maxSize);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(cache)//设置缓存
.build();
POST方式提交表单:登录
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("username", "qawsedrf") //用户名和密码可用 zhangsan12 123456
.add("password", "qazwsxedc")
.build();
Request request = new Request.Builder()
.url("https://www.wanandroid.com/user/login")
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage()); //加断点跟踪
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String str = response.body().string();
Log.i(TAG, "onResponse成功: " + str);
//json解析
LoginResultBean loginResultBean = new Gson().fromJson(str, LoginResultBean.class);
int errorCode = loginResultBean.getErrorCode();
//把errorCode发给handler判断登录 成功还是失败
handler.sendMessage(handler.obtainMessage(1, errorCode));
}
});
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
int errorCode = (int) msg.obj;
if (errorCode == 0) {//成功,跳转到成功页面
startActivity(new Intent(MainActivity.this, LoginSuccessActivity.class));
} else {
Toast.makeText(MainActivity.this, "账号密码不匹配!请检查",Toast.LENGTH_SHORT).show();
}
}
}
};
POST请求上传文件
// String filePath = Environment.getExternalStorageDirectory().getPath() + "/mm.png"; //通过代码获得存储路径
//File f2 = new File(filePath);
OkHttpClient okHttpClient = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/octet-stream");
// File file = new File("/storage/emulated/legacy/mm.png");//模拟器的路径
File file = new File("/storage/emulated/0/mm.png");//真机的路径
if (file.exists()) {//判断图片文件是否存在 file.exists() true表示存在
RequestBody requestBody = RequestBody.create(mediaType, file);
MultipartBody multipartBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), requestBody)
.build();
Request request = new Request.Builder()
.url("http://yun918.cn/study/public/file_upload.php")
.post(multipartBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
String str = e.getMessage();
Log.i(TAG, "onResponse上传失败: " + str);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String str = response.body().string();
Log.i(TAG, "onResponse上传成功: " + str);
//得到的是一个 字符串 分三行,最后一行是一个json串,所以用 换行符\n 切割
String[] split = str.split("\n");
str = split[split.length - 1];//得到第三个结果 json串 也可以用gson解析了
//UploadResultBean 是由成功的json串生成的解析类
UploadResultBean uploadResultBean = new Gson().fromJson(str, UploadResultBean.class);
int code = uploadResultBean.getCode();
if (code == 200) {//表示成功,继续得到 url 不然,失败,不要进行处理
String url = (String) uploadResultBean.getData().getUrl();
Log.i(TAG, "onResponse 图片的路径: " + url);
handler.sendMessage(handler.obtainMessage(2, url));
}
}
});
}
Handler handler = new Handler() {
String url = (String) msg.obj;
Glide.with(MainActivity.this).load(url).into(mHeaderImg);//加载为头像
}
header请求头:有些请求需要添加请求头header,服务器才能通过请求
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.get()
.url(headerUrl)
.header("Authorization", "APPCODE db33b75c89524a56ac94d6519e106a59")//设置请求头,已过期不可用
// .addHeader("Authorization","APPCODE db33b75c89524a56ac94d6519e106a59")//设置请求头 两种都可以
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "onResponse: " + response.body().string());
String s = "";
}
});
其他
1.推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。
2.每一个Call(其实现是RealCall)只能执行一次,否则会报异常,具体参见 RealCall#execute()
3.response.body().string()只调用一次
HttpURLconnection及OkHttp3的对比分析
1,HttpUrlConnection,google官方提供的用来访问网络,但是实现的比较简单,只支持1.0/1.1
2,并没有多路复用,如果碰到app大量网络请求的时候,性能比较差,
3,HttpUrlConnection底层也是用Socket来实现的
4,OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。
5,OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,OkHttp更强大,
6,HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但OkHttp用的是sink和source,这两个是在Okio这个开源库里的, feredSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink)
7,多个相同host和port的stream可以共同使用一个socket,而RealConnection就是处理连接的,那也就是说一个RealConnection上可能有很多个Stream
8,OkHttp代码比HttpURLConnection精简的多
四 OkHttp的封装
推荐用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。让 OkHttpClient 保持单例,对OkHttp进一步封装
public class OkHttpUtil {
private static OkHttpUtil okHttpUtil;
private final OkHttpClient okHttpClient;
private OkHttpUtil(){
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5,TimeUnit.SECONDS)
.writeTimeout(5,TimeUnit.SECONDS)
.build();
}
//单例模式 保证只有一个OkHttpUtil对象,同时只有一个okHttpClient对象
public static OkHttpUtil getInstance(){
if(okHttpUtil == null){
synchronized (OkHttpUtil.class){
if (okHttpUtil == null) {
okHttpUtil = new OkHttpUtil();
}
}
}
return okHttpUtil;
}
//异步 get 无参,参数在url中
public void get(String url, ResultCallback resultCallback){
callBackIsNull(resultCallback);
Request request = getRequestGet(url);
enqueue(resultCallback,request);
}
//异步 post 有参 ,参数
public void postString(String url, String data, ResultCallback resultCallback){
callBackIsNull(resultCallback);
MediaType type = MediaType.parse("text/x-markdown;charset=utf-8");
RequestBody body = RequestBody.create(type,data);
Request request = getRequestPost(url, body);
enqueue(resultCallback,request);
}
public void postForm(String url, Map<String, String> data, ResultCallback resultCallback){
callBackIsNull(resultCallback);
FormBody.Builder builder = new FormBody.Builder();
Set<Map.Entry<String, String>> entries = data.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
builder.add(key, value);
}
RequestBody body = builder.build();
Request request = getRequestPost(url, body);
enqueue(resultCallback, request);
}
public void postFile(String url, String filePath, ResultCallback resultCallback){
callBackIsNull(resultCallback);
MediaType type = MediaType.parse("application/octet-stream");//设置请求数据类型 是数据流
// File file = new File("/storage/emulated/legacy/mm.png");//上传的的文件
File file = new File(filePath);
if(file.exists()){
RequestBody fileBody = RequestBody.create(type, file);//创建带文件的请求体对象
RequestBody body = new MultipartBody.Builder() //通过分块的流数据体,结合带文件的请求体对象 创建最终的 form表单格式的请求体 body
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), fileBody)
.build();
Request request = getRequestPost(url, body);
enqueue(resultCallback, request);
}
}
//自定义回调接口,供Callback使用
public interface ResultCallback{
//失败的回调
void onFailure(IOException e);
//成功的回调
void onResponse(Response response) throws IOException;
}
//判断resultCallback是否为空
private void callBackIsNull(ResultCallback resultCallback) {
//判断callBack是否为null,
if (resultCallback == null){
throw new IllegalArgumentException("callBack is null");
}
}
//把异步执行的具体操作enqueue提取出来
private void enqueue(final ResultCallback resultCallback, Request request){
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
resultCallback.onFailure(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
resultCallback.onResponse(response);
}
});
}
//获取get请求的request对象
private Request getRequestGet(String url){
return new Request.Builder()
.get()
.url(url)
.build();
}
//获取post请求的request对象
private Request getRequestPost(String url, RequestBody requestBody){
return new Request.Builder()
.url(url)
.post(requestBody)
.build();
}
}
调用:
OkHttpUtil.getInstance().get(headerUrl, new OkHttpUtil.ResultCallback() {
@Override
public void onFailure(IOException e) {
}
@Override
public void onResponse(Response response) throws IOException {
}
});
本文地址:https://blog.csdn.net/weixin_44832024/article/details/107942022
推荐阅读
-
PHP中trim()函数简单使用指南,trim使用指南_PHP教程
-
asp.net(c#)下Jmai去说明 使用与下载
-
使用tesserocr.file_to_text("path")报错Failed to init API, possibly an invalid tessdata path: C:\\
-
(一)php的基本知识和一些注意点_PHP教程
-
PHP V5.2新增功能之第1部分:使用新的内存管理器_PHP教程
-
android使用surfaceview+MediaPlayer播放视频
-
EasyNVR网页摄像机无插件H5、谷歌Chrome直播方案-Onvif(一)使用Onvif协议进行设备发现以及指定设备信息探测
-
php使用fopen创建utf8编码文件的方法
-
Android基础控件RadioGroup使用方法详解
-
php中 预处理与事务同时使用