OkHttp使用笔记

1.首先添加依赖库

compile 'com.squareup.okhttp3:okhttp:3.5.0'
compile 'com.squareup.okio:okio:1.11.0'
compile 'com.facebook.stetho:stetho-okhttp3:1.4.2'

上面引入了三个库,分别是okhttp框架包,okhttp操作流的包,最后一个是facebook公司开发的用于检测okhttp日志的插件包,用于在chrome浏览器中调试okhttp网络访问数据非常方便。

stetho需要在Application中初始化

Stetho.initializeWithDefaults(this);

然后在chrome中输入地址:chrome://inspect/

2.okhttp的Get请求+封装

本身OkHttp的请求分为三步:
1.拿到OkHttpClient

OkHttpClient client = new OkHttpClient();

2.构造Request对象

Request request = new Request.Builder()
                .get()
                .url("https:www.baidu.com")
                .build();

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来处理

但每次都写这样的四步对于程序员来说肯定是痛苦的,所以下面进行封装处理,新建一个OkHttpUtils工具类用于统一的处理网络请求

package cq.cake.okhttpdemo;

import android.content.Context;

import com.facebook.stetho.okhttp3.StethoInterceptor;

import java.io.File;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;


/**
 * MyApplication --  cq.cake.okhttpdemo
 * Created by Small Cake on  2016/12/26 16:02.
 * 全局统一使用的OkHttpClient工具
 */

public class OkHttpUtils {

    public static final long DEFAULT_READ_TIMEOUT_MILLIS = 15 * 1000;
    public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20 * 1000;
    public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 20 * 1000;
    private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
    private static volatile OkHttpUtils sInstance;
    private OkHttpClient mOkHttpClient;

    private OkHttpUtils() {


        mOkHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                //FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
                .addNetworkInterceptor(new StethoInterceptor())
                //http数据log,日志中打印出HTTP请求&响应数据
                .addInterceptor(new LoggingInterceptor())
                .build();


    }

    public static OkHttpUtils getInstance() {
        if (sInstance == null) {
            synchronized (OkHttpUtils.class) {
                if (sInstance == null) {
                    sInstance = new OkHttpUtils();
                }
            }
        }
        return sInstance;
    }

    public OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    public void setCache(Context appContext) {
        final File baseDir = appContext.getApplicationContext().getCacheDir();
        if (baseDir != null) {
            final File cacheDir = new File(baseDir, "HttpResponseCache");
            mOkHttpClient.newBuilder().cache((new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE)));
        }
    }


    public static void getAsyn(String url, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }
 
}

这样每次访问数据的时候我们只需要,通过在Activity中调用getAsyn方法就可以了,方便很多。

OkHttpUtils.getAsyn("http://m.yuzetianxia.com", new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
//                System.out.println(response.body().string().toString());
               final String str = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvView.setText(decodeUnicode(str));
                    }
                });
            }
        });

注意:
这里返回的数据因为是异步请求,所以是运行在子线程中的,那么想要显示在UI控件上就要用到runOnUiThread方式,或者Handler。

3.okhttp的POST请求+封装

有了get请求的封装,post也是类似的封装,不过多加了一个请求体RequestBody,在Activity中请求的时候我们是用的RequestBody的子类FormBody.Builder构造来写入键值对的,不再使用Map的方式!
在OkHttpUtils中加入方法:

public static void postAsyn(String url, RequestBody body, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).post(body).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }

然后在Activity中使用:



        FormBody.Builder builder = new FormBody.Builder();
        builder.add("account","888888");
        builder.add("pwd","123456");

      OkHttpUtils.postAsyn("http://www.baidu.com/Api/Login/doLogin/",builder.build(),new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
//                Log.i("post>>>>",response.body().string().toString());
                final String str = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvView.setText(decodeUnicode(str));
                    }
                });
            }
        });

4.okhttp的上传文件+封装

在OkHttpUtils中加入方法:

public static void upFile(String url, RequestBody body, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).post(body).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }

在使用的时候需要自己构建文件File并用MultipartBody构建参数

int time = TimeUtils.getTime();

        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("mid", "3")
                .addFormDataPart("time", time+"")
                .addFormDataPart("sign", MD5.getSign("3", time))
                .addFormDataPart("pic", "1.png", RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();

        OkHttpUtils.upFile("http://m.hxfuuu.cn/Api/Manage/setHeadpic",muiltipartBody,new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
//                Log.i("post>>>>",response.body().string().toString());
                final String str = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvView.setText(decodeUnicode(str));
                    }
                });
            }
        });

5.okhttp的下载文件+封装

在OkHttpUtils中加入方法:

public static void getDownload(String url, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }

在得到回调结果的时候对流进行处理,不再是单纯的数据。

OkHttpUtils.getDownload("http://m.hxfuuu.cn/Public/upload/headpic/3.jpg", new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //1.拿到下载文件的总长度
                final long total = response.body().contentLength();
                //2.在while循环中每次递增我们读取的buf的长度
                long sum = 0L;
                //拿到字节流
                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);
                    //每次递增
                    sum += len;
                    final long finalSum = sum;
                    Log.d("pyh1", "onResponse: " + finalSum + "/" + total);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //将进度设置到TextView中
                            tvView.setText(finalSum + "/" + total);
                        }
                    });
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                fos.flush();

                //关闭流
                fos.close();
                is.close();
            }
        });

写了这些后,你会发现其实下载文件getDownload方法和getAsyn方法,上传文件upFile方法和postAsyn方法内部方法体都是一样的。确实是一样的,不同的是在Activity中发出请求前的处理和得到数据后的处理不同而已。所以后面两个方法其实都可以省略,关键要知道在具体的请求时应该做什么就可以了!

6.okhttp的https请求

okhttp默认是支持https请求的,比如你用get方法去访问https://www.baidu.com是可以直接访问的,但是针对有的个人自建证书或不被认可的证书,就需要特殊处理一下了!
我们一般是把证书(.crt文件)放在资源文件assets下,然后在okhttp的配置中去配置sslSocketFactory用于访问自建的证书。
下面是完整的OkHttpUtils封装类,方便大家根据自己的需要修改。

package cq.cake.okhttpdemo;

import android.content.Context;

import com.facebook.stetho.okhttp3.StethoInterceptor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;


/**
 * MyApplication --  cq.cake.okhttpdemo
 * Created by Small Cake on  2016/12/26 16:02.
 * 全局统一使用的OkHttpClient工具
 */

public class OkHttpUtils {

    public static final long DEFAULT_READ_TIMEOUT_MILLIS = 15 * 1000;
    public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20 * 1000;
    public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 20 * 1000;
    private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
    private static volatile OkHttpUtils sInstance;
    private OkHttpClient mOkHttpClient;

    /**
     * 需要访问https,且证书只能在本地访问
     * @param context
     */
    private OkHttpUtils(Context context) {
        X509TrustManager trustManager = null;
        SSLSocketFactory sslSocketFactory = null;
        try {
        final InputStream inputStream;
            inputStream = context.getAssets().open("yuzetianxia.com.crt"); // 得到证书的输入流


                trustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{trustManager}, null);
                sslSocketFactory = sslContext.getSocketFactory();

            } catch (GeneralSecurityException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
            e.printStackTrace();
        }
        mOkHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                //FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
                .addNetworkInterceptor(new StethoInterceptor())
                //http数据log,日志中打印出HTTP请求&响应数据
                .addInterceptor(new LoggingInterceptor())
                .sslSocketFactory(sslSocketFactory, trustManager)
                .build();


    }
    private OkHttpUtils() {


        mOkHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                //FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
                .addNetworkInterceptor(new StethoInterceptor())
                //http数据log,日志中打印出HTTP请求&响应数据
                .addInterceptor(new LoggingInterceptor())
                .build();


    }

    public static OkHttpUtils getInstance(Context context) {
        if (sInstance == null) {
            synchronized (OkHttpUtils.class) {
                if (sInstance == null) {
                    sInstance = new OkHttpUtils(context);
                }
            }
        }
        return sInstance;
    }
    public static OkHttpUtils getInstance() {
        if (sInstance == null) {
            synchronized (OkHttpUtils.class) {
                if (sInstance == null) {
                    sInstance = new OkHttpUtils();
                }
            }
        }
        return sInstance;
    }

    public OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    public void setCache(Context appContext) {
        final File baseDir = appContext.getApplicationContext().getCacheDir();
        if (baseDir != null) {
            final File cacheDir = new File(baseDir, "HttpResponseCache");
            mOkHttpClient.newBuilder().cache((new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE)));
        }
    }

    public static void getAsyn(Context context,String url, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance(context).getOkHttpClient();
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }
    public static void postAsyn(Context context,String url, RequestBody body, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance(context).getOkHttpClient();
        Request request = new Request.Builder().url(url).post(body).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }

        public static void getAsyn(String url, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }
    public static void postAsyn(String url, RequestBody body, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).post(body).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }
    public static void upFile(String url, RequestBody body, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).post(body).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }
    public static void getDownload(String url, Callback callBack){
        OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(callBack);
    }


    /**
     * 以流的方式添加信任证书
     */
    /**
     * Returns a trust manager that trusts {@code certificates} and none other. HTTPS services whose
     * certificates have not been signed by these certificates will fail with a {@code
     * SSLHandshakeException}.
     * <p>
     * <p>This can be used to replace the host platform's built-in trusted certificates with a custom
     * set. This is useful in development where certificate authority-trusted certificates aren't
     * available. Or in production, to avoid reliance on third-party certificate authorities.
     * <p>
     * <p>
     * <h3>Warning: Customizing Trusted Certificates is Dangerous!</h3>
     * <p>
     * <p>Relying on your own trusted certificates limits your server team's ability to update their
     * TLS certificates. By installing a specific set of trusted certificates, you take on additional
     * operational complexity and limit your ability to migrate between certificate authorities. Do
     * not use custom trusted certificates in production without the blessing of your server's TLS
     * administrator.
     */
    private X509TrustManager trustManagerForCertificates(InputStream in)
            throws GeneralSecurityException {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        Collection<? extends java.security.cert.Certificate> certificates = certificateFactory.generateCertificates(in);
        if (certificates.isEmpty()) {
            throw new IllegalArgumentException("expected non-empty set of trusted certificates");
        }

        // Put the certificates a key store.
        char[] password = "password".toCharArray(); // Any password will work.
        KeyStore keyStore =  newEmptyKeyStore(password);
        int index = 0;
        for (java.security.cert.Certificate certificate : certificates) {
            String certificateAlias = Integer.toString(index++);
            keyStore.setCertificateEntry(certificateAlias, certificate);
        }

        // Use it to build an X509 trust manager.
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
                KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                    + Arrays.toString(trustManagers));
        }
        return (X509TrustManager) trustManagers[0];
    }
    /**
     * 添加password
     * @param password
     * @return
     * @throws GeneralSecurityException
     */
    private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 这里添加自定义的密码,默认
            InputStream in = null; // By convention, 'null' creates an empty key store.
            keyStore.load(in, password);
            return keyStore;
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }


}

使用的时候需要多传入一个资源文件

OkHttpUtils.getAsyn(this,"https://m.yuzetianxia.com", new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
//                System.out.println(response.body().string().toString());
               final String str = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvView.setText(decodeUnicode(str));
                    }
                });
            }
        });

未完待续。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容