text 使用OkHttp高效开发调试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了text 使用OkHttp高效开发调试相关的知识,希望对你有一定的参考价值。

使用OkHttp高效开发调试
96  laizuling 
2016.08.26 15:41* 字数 705 阅读 7376评论 12喜欢 23
本文使用的OkHttp版本是okhttp3,使用okhttp2的情况稍有不同,使用okhttp2的同学替换为okhttp3也不麻烦,API都很接近;当然,如果想要使用okhttp2使用stetho & 拦截器也是可以的,可以参考官方文档。

先贴出Gradle依赖,基本上我使用的都是最新版本的库。

def okhttp3Version = '3.4.1'
compile('com.squareup.okhttp3:okhttp:' + okhttp3Version)
compile('com.squareup.okhttp3:logging-interceptor:' + okhttp3Version) 
testCompile('com.squareup.okhttp3:mockwebserver:' + okhttp3Version)
compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'    
1. 定义一个全局的OkHttp请求单例类,全局统一使用单一OkHttpClient:
/** 
  * 全局统一使用的OkHttpClient工具,okhttp版本:okhttp3 
  */
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() {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        //包含header、body数据
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        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(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)));
        }
    }
}
在所有需要用到OkHttpClient的地方使用以下代码,全局使用同一个OkHttpClient实例:

OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
全局使用一个OkHttpClient的原因是所有请求没必要创建多个请求客户端实例,一个好处是节省内存,另外一个是全局设定了一些监控工具,如Facebook的Stetho 和 OkHttp自带的HttpLoggingInterceptor,你就可以监管你的所有Http请求。

2. HttpLoggingInterceptor Log直接观察HTTP请求&响应数据
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//包含header、body数据
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

//在build OkHttpClient的时候加入Log拦截器
OkHttpClient.Builder().addInterceptor(loggingInterceptor)
HttpLoggingInterceptor的效果如下图,开发时如果需要看整个App的所有请求及相应,可以使用okhttp关键字过滤。

OkHttp3拦截器Log打印.png
在实例化你的HttpLoggingInterceptor的时候传入一个Logger参数,可以定制化OkHttp输出的格式化http请求体&响应体log。

3. Facebook强大的监测工具:Stetho
3.1 在build OkHttpClient时需要添加网络拦截器
OkHttpClient.Builder().addNetworkInterceptor(new StethoInterceptor())
3.2 在Application的OnCreate中初始化
//FaceBook调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
Stetho.initializeWithDefaults(this);
3.3 连接手机,在Chrome中打开<a>chome://inspect/#devices</a>
看到如下界面,则代表监测成功,如果没有App显示,那应该就是忘了在Application中初始化Stetho

chrome://inspect/#devices.png
3.4 查看网络请求
请求概览
HTTP响应body
3.5查看数据库
查看数据库
查看SharePreferences也同理,点击Local Storage就是你的App的所有SharePreferences。

4. 自定义OkHttp拦截器
当你与服务端对接的时候,当服务端功能还没开发完成,而你等待着服务器的接口测试时,自定义拦截器就可以帮你无需等待服务端完成功能先进行开发。
步骤:
1)先与服务端协商接口返回数据格式,拿到协商的数据,写一些假数据,每个接口对应建立一个JSON文本文件,里面放接口定义的数据。

数据文件,服务端同学提供测试数据.png
2)自定义拦截器

/** * 自定义okhttp拦截器,可定制接口伪造Http响应数据 */
public final class MockDataApiInterceptor implements Interceptor {    
public static final String TAG = MockDataApiInterceptor.class.getSimpleName();
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = null;
        String path = chain.request().url().uri().getPath();
        LogUtil.d(TAG, "intercept: path=" + path);
        response = interceptRequestWhenDebug(chain, path);
        if (null == response) {
            LogUtil.i(TAG, "intercept: null == response");
            response = chain.proceed(chain.request());
        }        return response;
    }

    /**
     * 测试环境下拦截需要的接口请求,伪造数据返回
     *
     * @param chain 拦截器链
     * @param path  请求的路径path
     * @return 伪造的请求Response,有可能为null
     */
    private Response interceptRequestWhenDebug(Chain chain, String path) {
        Response response = null;
        if (BuildConfig.DEBUG) {
            Request request = chain.request();
            if (path.equalsIgnoreCase("/api/event")) {
                //活动列表接口
                response = getMockEventListResponse(request);
            } else if (path.startsWith("/api/event/")) {
                //活动详情接口
                response = getMockEventDetailResponse(request);
            } 
        }
        return response;
    }

    /**
     * 伪造活动详情接口响应
     *
     * @param request 用户的请求
     * @return 伪造的活动详情HTTP响应
     */
    private Response getMockEventDetailResponse(Request request) {
        Response response;
        String data = MockDataGenerator.getMockDataFromJsonFile("mock/EventDetail.json");
        response = getHttpSuccessResponse(request, data);
        return response;
    }

    /**
     * 伪造活动列表接口响应
     *
     * @param request 用户的请求
     * @return 伪造的活动列表HTTP响应
     */
    private Response getMockEventListResponse(Request request) {
        Response response;
        String data = MockDataGenerator.getMockDataFromJsonFile("mock/EventList.json");
        response = getHttpSuccessResponse(request, data);
        return response;
    }
   
     /**
     * 根据数据JSON字符串构造HTTP响应,在JSON数据不为空的情况下返回200响应,否则返回500响应
     *
     * @param request  用户的请求
     * @param dataJson 响应数据,JSON格式
     * @return 构造的HTTP响应
     */
    private Response getHttpSuccessResponse(Request request, String dataJson) {
        Response response;
        if (TextUtils.isEmpty(dataJson)) {
            LogUtil.w(TAG, "getHttpSuccessResponse: dataJson is empty!");
            response = new Response.Builder()
                    .code(500)
                    .protocol(Protocol.HTTP_1_0)
                    .request(request)
                    //必须设置protocol&request,否则会抛出异常
                    .build();
        } else {
            response = new Response.Builder()
                    .code(200)
                    .message(dataJson)
                    .request(request)
                    .protocol(Protocol.HTTP_1_0)
                    .addHeader("Content-Type", "application/json")
                    .body(ResponseBody.create(MediaType.parse("application/json"), dataJson))                    .build();
        }
        return response;
    }

    private Response getHttpFailedResponse(Chain chain, int errorCode, String errorMsg) {
        if (errorCode < 0) {
            throw new IllegalArgumentException("httpCode must not be negative");
        }
        Response response;
        response = new Response.Builder()
                .code(errorCode)
                .message(errorMsg)
                .request(chain.request())
                .protocol(Protocol.HTTP_1_0)
                .build();
        return response;
    }
}
接口返回什么数据都由你自己定义,即使是Http的响应码等。这里的响应body我们使用服务端同学给我们提供的JSON文件

3)在build OkHttpClient时添加自定义拦截器

OkHttpClient.Builder().addInterceptor(new MockDataApiInterceptor())
4)进行开发,完善数据解析逻辑等,与View结合等。

5. OkHttp与Retrofit/RxJava组合请求
准备写一篇文章详细讲

以上是关于text 使用OkHttp高效开发调试的主要内容,如果未能解决你的问题,请参考以下文章

14 | Square 现代高效的 HTTP 客户端 okhttp基本用法

Retrofit VS OkHttp,谁是最强网络开发框架?

OkHttp - 启用日志

Android OkHttp, 一行代码 OkHttp提升请求稳定性

Java EE 架构设计——基于okhttp3 的网络框架设计

[RK3568 Android11] 教程之高效率开发调试方法