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公司開發(fā)的用于檢測okhttp日志的插件包,用于在chrome瀏覽器中調(diào)試okhttp網(wǎng)絡(luò)訪問數(shù)據(jù)非常方便。

stetho需要在Application中初始化

Stetho.initializeWithDefaults(this);

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

2.okhttp的Get請求+封裝

本身OkHttp的請求分為三步:
1.拿到OkHttpClient

OkHttpClient client = new OkHttpClient();

2.構(gòu)造Request對象

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

3 . 將Request封裝為Call

Call call = client.newCall(request);

4 . 根據(jù)需要調(diào)用同步或者異步請求方法
//同步調(diào)用,返回Response,會拋出IO異常
Response response = call.execute();
//異步調(diào)用,并設(shè)置回調(diào)函數(shù)
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);
        }
    });
}

});

注意:
同步調(diào)用會阻塞主線程,一般不適用
異步調(diào)用的回調(diào)函數(shù)是在子線程,我們不能在子線程更新UI,需要借助于runOnUiThread()方法或者Handler來處理

但每次都寫這樣的四步對于程序員來說肯定是痛苦的,所以下面進(jìn)行封裝處理,新建一個OkHttpUtils工具類用于統(tǒng)一的處理網(wǎng)絡(luò)請求

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.
 * 全局統(tǒng)一使用的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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
                .addNetworkInterceptor(new StethoInterceptor())
                //http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
                .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);
    }
 
}

這樣每次訪問數(shù)據(jù)的時候我們只需要,通過在Activity中調(diào)用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));
                    }
                });
            }
        });

注意:
這里返回的數(shù)據(jù)因?yàn)槭钱惒秸埱螅允沁\(yùn)行在子線程中的,那么想要顯示在UI控件上就要用到runOnUiThread方式,或者Handler。

3.okhttp的POST請求+封裝

有了get請求的封裝,post也是類似的封裝,不過多加了一個請求體RequestBody,在Activity中請求的時候我們是用的RequestBody的子類FormBody.Builder構(gòu)造來寫入鍵值對的,不再使用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);
    }

在使用的時候需要自己構(gòu)建文件File并用MultipartBody構(gòu)建參數(shù)

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()
                //一定要設(shè)置這句
                .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);
    }

在得到回調(diào)結(jié)果的時候?qū)α鬟M(jìn)行處理,不再是單純的數(shù)據(jù)。

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循環(huán)中每次遞增我們讀取的buf的長度
                long sum = 0L;
                //拿到字節(jié)流
                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() {
                            //將進(jìn)度設(shè)置到TextView中
                            tvView.setText(finalSum + "/" + total);
                        }
                    });
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                fos.flush();

                //關(guān)閉流
                fos.close();
                is.close();
            }
        });

寫了這些后,你會發(fā)現(xiàn)其實(shí)下載文件getDownload方法和getAsyn方法,上傳文件upFile方法和postAsyn方法內(nèi)部方法體都是一樣的。確實(shí)是一樣的,不同的是在Activity中發(fā)出請求前的處理和得到數(shù)據(jù)后的處理不同而已。所以后面兩個方法其實(shí)都可以省略,關(guān)鍵要知道在具體的請求時應(yīng)該做什么就可以了!

6.okhttp的https請求

okhttp默認(rèn)是支持https請求的,比如你用get方法去訪問https://www.baidu.com是可以直接訪問的,但是針對有的個人自建證書或不被認(rèn)可的證書,就需要特殊處理一下了!
我們一般是把證書(.crt文件)放在資源文件assets下,然后在okhttp的配置中去配置sslSocketFactory用于訪問自建的證書。
下面是完整的OkHttpUtils封裝類,方便大家根據(jù)自己的需要修改。

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.
 * 全局統(tǒng)一使用的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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
                .addNetworkInterceptor(new StethoInterceptor())
                //http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
                .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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
                .addNetworkInterceptor(new StethoInterceptor())
                //http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
                .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()); // 這里添加自定義的密碼,默認(rèn)
            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));
                    }
                });
            }
        });

未完待續(xù)。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容