当前位置: 移动技术网 > 移动技术>移动开发>Android > OkHttp基本使用

OkHttp基本使用

2020年08月14日  | 移动技术网移动技术  | 我要评论
简介OkHttp是一个高效的HTTP客户端,它有以下默认特性:支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接连接池减少请求延时透明的GZIP压缩减少响应数据的大小缓存响应内容,避免一些完全重复的请求当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS

简介

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网