Android框架-OkHttp与OKio

 


1.Okio

1.1 输入与输出

         以前总是搞不清输入与输出,是因为处的位置不对。输入与输出是站在程序自身的角度来看的。

1.2 历史

        java.io --> java.nio --> okio

1.3 ByteString

1.4 Buffer

 

 

2.OkHttp

2.1 简介

       OKHttp是由Square公司开发。使用的时候需要添加依赖库,如下所示:

compile 'com.squareup.okhttp3:okhttp:3.14.0'

//注意:不同的okhttp版本需要的minSdkVersion不一样,可能会报如下错误:
java.lang.BootstrapMethodError: Exception from call site #4 bootstrap method,将okhttp版本调低即可。

        添加上述依赖会下载两个库:OKHttp与Okio库,后者是前者的通信基础

       使用步骤如下所示:

1. 不管是异步还是通过都是先创建OkHttpClient,建议全局只有一个

2. 然后使用Request的内部类Builder来创建一个Request,并设置网址

3. 调用newCall的newCall方法传入request

4. 调用newCall方法返回的Call对象上面的execute或者enqueue来执行这个请求

5. 调用response的isSuccessful方法来判断是否请求成功

6. 调用response的body上面的string方法来获取一个字符串结果

我还可以通过response.body()的其他方法拿到bytes数组,输入流等信息。

其中我们拿到了输入流或者bytes数组我们可以用来手动解码一张图片,或者下载一个文件。

2.2 OKHttpClient

2.2.1 OkHttpClient源码构造

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    int callTimeout;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    ...
    public OkHttpClient() {
        this(new Builder());
    }
    OkHttpClient(Builder builder) {
        ...
    }
    ...
    public static final class Builder {
        int callTimeout;
        int connectTimeout;
        int readTimeout;
        int writeTimeout;
        ...
        public Builder() {
            //超时时间
            callTimeout = 0;
            connectTimeout = 10_000;
            readTimeout = 10_000;
            writeTimeout = 10_000;
            ...
        }
        public OkHttpClient build() {
            return new OkHttpClient(this);
        }
    }
}

2.2.2 实例创建方式

        从上面的源码可以看出,OkHttpClient有两种创建方式,如下所示:

//方式1:
OkHttpClient client= new OkHttpClient();

//方式2
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.build();

2.2.3 配置管理

      1)配置公共请求超时时间

OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)   //连接超时时间
                .writeTimeout(10, TimeUnit.SECONDS)     //数据发送到服务端超时时间
                .readTimeout(30, TimeUnit.SECONDS)      //从服务端下载数据到本地超时时间
                .build();

//需要注意的是,如果上传或者下载文件则需要将时间调长一点

    2)配置单个请求超时时间      

//这里创建了一个默认的Okhttp
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
    //这个请求使用默认配置
    Request request = new Request.Builder()
        .url("http://xxx") // This URL is served with a 1 second delay
        .build();
    try {
        // 从client上创建一个浅拷贝,然后在更改配置,他只影响使用它发送的请求
        OkHttpClient copy = client.newBuilder()
                .readTimeout(500, TimeUnit.MILLISECONDS)
                .build();
        Response response = copy.newCall(request).execute();
        System.out.println("Response 1 succeeded: " + response);
    } catch (IOException e) {
        System.out.println("Response 1 failed: " + e);
    }
    try {
        // 这里又创建一个okhttp的拷贝,并配置他的超时时间为3秒
        OkHttpClient copy = client.newBuilder()
                .readTimeout(3000, TimeUnit.MILLISECONDS)
                .build();
        Response response = copy.newCall(request).execute();
        System.out.println("Response 2 succeeded: " + response);
    } catch (IOException e) {
        System.out.println("Response 2 failed: " + e);
    }
}

 2.3 Request静态内部类Builder

      创建Builder如下所示:

//创建Builder对象
Request.Builder builder = new Request.Builder;

2.4 Request

2.4.1 组成

         如果想要发送一条Http请求,则需要创建一个Request对象。一个Request包含如下内容:

URL 
method 
headers 
body 

2.4.2 Get请求

       Get方式如下所示:

//创建Builder对象
Request.Builder builder = new Request.Builder();
//传入URL地址
builder.url("http://www.baidu.com");
//创建Request对象
Request request = builder.build();

2.4.3 Post  请求     

       Post请求用于向服务器提交数据,并得到返回的结果数据。Post请求会比Get请求复杂一点,需要先构建出一个RequestBody对象来存放待提交的参数,如下所示:具体的要参照服务端给出的接口样式

//创建RequestBody 对象
RequestBody requestBody = new FormBody.Builder()
                .add("username","admin")
                .add("password","23456")
                .build();
//创建Builder对象
Request.Builder builder = new Request.Builder();
//传入URL地址
builder.url("http://www.baidu.com");
//将requestBody传入进去
builder.post(requestBody);
//创建Request对象
Request request = builder.build();

2.4.4 源码

          源码如下:

public final class Request {
    ...... 
    
    //静态内部类
    public static class Builder {
        @Nullable HttpUrl url;
        String method;
        Headers.Builder headers;
        @Nullable RequestBody body;

        public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();
        }

        public Request build() {
          if (url == null) throw new IllegalStateException("url == null");
          return new Request(this);
        }
        ......
      }
}

        上述代码只是创建了一个空的Request对象,并没有什么实际作用

2.5 Call

2.5.1 创建

        有了Request,就需要OkHttpClient的newCall()方法来创建一个Call对象,如下所示:

//创建Call对象
Call call = okHttpClient.newCall(request);

       源码如下:

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

2.5.2 请求类型

        Call有两种类型:同步请求与异步请求。同步请求需要在子线程里面进行

同步请求

通过call.execute();请求会阻塞在这里,直到服务器返回数据。

 

异步请求

通过call.enqueue(Callback callback)方法。

void enqueue(Callback responseCallback);

2.5.3 同步请求

     1)源码

public interface Call extends Cloneable { 
    ......
    Response execute() throws IOException;
    ......
}

     2)使用

//开启子线程
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            //开启同步请求
            Response response = okHttpClient.newCall(request).execute();
            ......
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

2.5.4 Callback异步请求

      1)源码

public interface Call extends Cloneable { 
    ......
    void enqueue(Callback responseCallback);
    ......
}

     2)使用

okHttpClient.newCall(request).enqueue(new Callback(){
    //请求失败调用
    @Override
    public void onFailure(Call call, IOException e) {

    }

    //其他情况下调用
    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
});

3)注意

       onFailure()和onResponse()方法不是在主线程里面,不能直接操作UI。

2.6 Response

2.6.1 创建

            有了Call对象,然后调用Call对象的execute()方法,来发送请求并获取服务器返回的数据,即Response。如下所示:

//获取到服务端返回的数据
try {
    Response response = call.execute();
    } catch (IOException e) {
        e.printStackTrace();
}

2.6.2 组成

        Response的组成如下所示:

1.code表示返回码,可通过response.code()得到
2.headers头部文件,可通过response.headers()得到
3.body响应体,可通过response.body()得到

2.6.3 获取Response具体内容

         通过如下代码获取服务端返回的数据的具体内容:

//获取Response的具体内容:
String responseData = response.body().string();

     注意:别使用toString()方法了。

1)string()方法源码

/**
   * Returns the response as a string decoded with the charset of the Content-Type header. If that
   * header is either absent or lacks a charset, this will attempt to decode the response body as
   * UTF-8.
   */
public final String string() throws IOException {
    return new String(bytes(), charset().name());
}

2)toString()方法源码

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

2.7 实现在子线程中更新UI的多种方法

       通常有如下三种方法:其原理都是向Android的主线程消息队列插入一条消息

1.使用Handler
2.如果是在Activity类里面中,使用runOnUiThread(Runnable action);方法
3.使用View上面的post方法

2.7.1 Handler

      首先在Activity中创建一个Handler:

//这样直接使用内部类的方式创建,会有内存泄漏
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //更新UI
        tv.setText(msg.obj.toString());
    }
};

      然后在异步回调中发送一个消息即可:

client.newCall(request).enqueue(new okhttp3.Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    
    }
    @Override
    public void onResponse(Call call, final Response response) throws IOException {
        String content = response.body().string();
        //注意一定要sendToTarget(),不然接收不到
        handler.obtainMessage(0, content).sendToTarget();
        //其实这里直接使用runOnUiThread()也可以,不用Handler,如后面所示
    }
});

2.7.2 runOnUiThread(Runnable action)

       使用这个方式的前提是:在Activity类中或者将context转为Activity       

client.newCall(request).enqueue(new okhttp3.Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    
    }
    @Override
    public void onResponse(Call call, final Response response) throws IOException {
        final String string = response.body().string();
        runOnUiThread(new Runnable() {
        @Override
        public void run() {
            tv.setText(string);
          }
        });
    }
});

2.7.3 View的post方法

client.newCall(request).enqueue(new okhttp3.Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    
    }
    @Override
    public void onResponse(Call call, final Response response) throws IOException {
        final String string = response.body().string();
        //post()方法
        textView.post(new Runnable() {
            @Override
            public void run() {
                textView.setText(content);
            }
        });
    }
});

2.8 传递请求参数

2.8.1 Get传递参数

        Get请求方式传递参数只能通过拼接URL来完成,如要请求如下地址:http://me.woblog.cn/?s=android&order=0

//将要传递的参数添加到Map中,比如:用户登录名,密码
HashMap<String, Object> params = new HashMap<>();
params.put("s","Android");
params.put("order",0);
//然后调用一个方法格式化参数
String url= formatParams("http://me.woblog.cn/",params);
Request request = new Request.Builder()
                        .url(url)
                        .build();

/**
 * 将Map的key和value拼接成key=value的格式
 * @param url
 * @param params
 * @return 参考值:http://me.woblog.cn/?order=0&s=Android&
 */
private String formatParams(String url, HashMap<String, Object> params) {
    StringBuilder sb = new StringBuilder();
    sb.append(url);
    sb.append("?");
    for (Map.Entry<String, Object> p : params.entrySet()) {
        sb.append(p.getKey());
        sb.append("=");
        try {
            sb.append(URLEncoder.encode(p.getValue().toString(),"utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e);
        }
        sb.append("&"); //这个&符号,最好在最后移除,因为末尾多一个
     }
     return sb.toString();
}

2.8.2 Post传递参数

     1)Post传递String

//这个类型要和服务端协定,不然他不知道怎么取该类型的数据
public static final MediaType MEDIA_TYPE_MARKDOWN
    = MediaType.parse("text/x-markdown; charset=utf-8");
//新建一段markdown文本
String postBody = ""
    + "Releases\\n"
    + "--------\\n"
    + "\\n"
    + " * 3333\\n"
    + " * 11111\\n"
    + " * 99999999\\n";
//通过RequestBody.create方法创建一个RequestBody
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody);
//通过post方法传入requestBody
Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(requestBody)
    .build();

     2)Post传递文件

        如下所示传递一个图片文件

//这个类型要和服务端协定,不然他不知道怎么取该类型的数据
public static final MediaType MEDIA_TYPE_JPG
                    = MediaType.parse("image/jpeg; charset=utf-8");
//根据File创建一个一个请求体
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JPG, new File("/sdcard/a.jpg"));
Request request = new Request.Builder()
                        .url("https://api.github.com/markdown/raw")
                        .post(requestBody)
                        .build();

    3)提交表单如上面所示

2.9 取消请求

2.10 OkHttp的优势

1.允许连接到同一个主机地址的所有请求提高请求效率
2.共享Socket减少了对服务器的请求次数
3.连接池减少了请求延时
4.缓存响应数据来减少重复的网络请求

 

 

3.OkHttp的高级使用

3.1 下载文件

3.2 拦截器

      拦截器是Okhttp中很强大的机制,可以用来监视,重写,重试调用请求。

3.3 缓存策略

3.4 设置代理

3.5 Cookie

4.OkHttp的封装

 

 

 

5.案例

5.1 通过同步请求方式

      注意:要开启子线程

5.1.1 效果图

        1)未请求之前

        2)请求之后

5.1.2 创建布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get"
        android:gravity="center"
        android:textSize="20sp"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>

5.1.3 处理业务

public class MainActivity extends AppCompatActivity {
    private final OkHttpClient okHttpClient = new OkHttpClient();
    private TextView contentTextView;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        contentTextView = findViewById(R.id.tv_content);
        button = findViewById(R.id.button);
        //按钮点击事件
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                synchronize();
            }
        });
    }

    /**
     * 同步请求
     */
    private void synchronize(){
        //开启子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                   OkHttpClient client = new OkHttpClient();
                   Request request = new Request.Builder()
                           .url("http://www.baidu.com")
                           .build();
                   Response response = client.newCall(request).execute();
                   final String responseData = response.body().string();
                        //在主线程中更新UI
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this,responseData,Toast.LENGTH_LONG);
                                contentTextView.setText(responseData);
                            }
                        });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

5.1.4 添加权限

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

5.2 通过异步请求方式

5.2.1 效果图

       1)未请求前

       2)请求后

5.2.2 布局

        布局跟同步请求方式一样

5.2.3 业务逻辑

public class MainActivity extends AppCompatActivity {
    private final OkHttpClient okHttpClient = new OkHttpClient();
    private TextView contentTextView;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        contentTextView = findViewById(R.id.tv_content);
        button = findViewById(R.id.button);
        //按钮点击事件
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                asynchronous();
            }
        });

    }

    /**
     * 异步请求
     */
    public void asynchronous(){
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //获取返回码
                int code = response.code();
                //获取头部信息
                Headers headers = response.headers();
                //获取内容信息类型
                String contentType = headers.get("Content-Type");
                //获取内容信息
                String content = response.body().string();
                final StringBuilder buf = new StringBuilder();
                buf.append("\ncode: " + code);
                buf.append("\nheaders: " + headers);
                buf.append("\ncontentType: " + contentType);
                buf.append("\ncontent: " + content);
                //主线程中更新UI
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        contentTextView.setText(buf.toString());
                    }
                });
            }
        });
    }

}

5.3.3 添加权限

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

 

6.去掉校验证书

如下:

/**
 * 获取OkHttpClient,去掉校验证书
 *
 * @return OkHttpClient
 */
public static OkHttpClient getUnsafeOkHttpClient() {
    try {
        final TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] chain, 
                    String authType) {
                 }
                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] chain, 
                    String authType) {
                }
                @Override        
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new java.security.cert.X509Certificate[]{};
                }
            }
        };

        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory);
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

        return builder.build();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

Feign-OkHttp是一个依赖库,用于在Spring Boot项目中集成OkHttp。你可以通过在pom.xml文件中添加以下依赖来使用Feign-OkHttp: ```xml <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency> ``` 在项目中引入Feign-OkHttp后,它会自动配置OkHttpClient并进行初始化。通过在application.yml文件中设置feign.okhttp.enabled=true来启用Feign-OkHttp自动配置。 在Feign-OkHttp中,连接池相关的参数如最大空闲连接数和连接最大空闲时间可以在application.yml文件中的feign.httpclient.max-connections和feign.httpclient.time-to-live属性中进行配置。 另外,连接超时的设置是通过application.yml文件中的feign.httpclient.connection-timeout属性进行配置。 需要注意的是,feign.httpclient.connection-timeout只对OkHttpClient的初始化阶段起作用。实际处理HTTP URL请求时,主要依据feign.Request.Options中的connectTimeoutMillis和readTimeoutMillis参数。如果feign.Request.Options中的connectTimeoutMillis和readTimeoutMillis与OkHttpClient实例中的不一致,则以feign.Request.Options中的参数为准。 这就是如何在Spring Boot项目中使用Feign-OkHttp集成OkHttp的方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring Cloud Open Feign系列【4】集成OkHttp及连接池配置详解](https://blog.csdn.net/qq_43437874/article/details/122169675)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Feign 如何使用 Okhttp 完成 HTTP URL 请求](https://blog.csdn.net/yangchao1125/article/details/104492547)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckyliuqs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值