okhttp是非常好用的Library,典型的例子就是在可汗学院Android版app上的应用。在此库上默认配置提供了重要的接口,下面是我们高效灵活运用此库的技巧和强有力的内省的一些步骤:
默认情况下,okhttp是不缓存响应允许缓存包括HTTP缓存控制头。因此,您的客户端可能会浪费时间和带宽,一次又一次的要求相同的资源,而不是简单地读一个缓存后的副本的初始响应。
配置com.squareup.okhttp.Cache 实例对象,启用告诉缓存的响应文件系统,并将其传递给OkhttpClient实例的setCache方法。你必须实例化Cache,传入缓存文件的目录和字节的最大值。响应的资源就会缓存到指定的目录当中。如果缓存的响应资源超过指定的缓存最大值,就会根据LRU的原则清除缓存。
由Jesse Wilson 推荐的,我们把数据缓存到由context.getCacheDir()得到的目录的子目录当中:
// Base directory recommended by http://stackoverflow.com/a/32752861/400717.
// Guard against null, which is possible according to
// https://groups.google.com/d/msg/android-developers/-694j87eXVU/YYs4b6kextwJ and
// http://stackoverflow.com/q/4441849/400717.
final @Nullable File baseDir = context.getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, "HttpResponseCache");
okHttpClient.setCache(new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE));
}
在 可汗学院的应用中,我们指定http_response_disk_cache_max_size为10*1024*1024,或者10MB。
Facebook中的Stetho是一个很可爱的库,它允许你使用Chrome浏览器开发工具来检查你的Android应用程序。
除了允许你检查SQLite数据库和应用程序的视图层次结构,Stetho 还允许你检查okhttp的每一个请求和响应
根据服务器返回的默许缓存的资源的所有HTTP header,来验证当有缓存的资源时是没有发送新的请求的。这种自我验证的过程是非常有用的。
使用Stetho 非常简单,只需要添加一个 StethoInterceptor 实例到网络拦截列表中就可以了:
okHttpClient.networkInterceptors().add(new StethoInterceptor());
然后,运行app,打开浏览器,并导航到chrome://inspect. app的设备信息和应用程序标识符就会展示出来。访问它的“inspect”链接打开开发工具,然后打开网络选项卡,开始监控okhttp请求。
如果你像我们一样,你可以使用Picasso 加载网络图片,或者使用Retrofit 简化发出的请求和解析响应。默认情况下,如果没有明确指定供内部使用的OkHttpClient, 这些库就会创建一个默认的。来自Picasso2.5.2版本的OkHttpDownLoader类中的:
private static OkHttpClient defaultOkHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setWriteTimeout(Utils.DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
return client;
}
Retrofit 也有类似的工厂方法创建自己的OkHttpClient。
图片是在app中将加载的最大资源之一。而Pocasso 会维护图片的LRU缓存,把图片完整的保存。当客户端使用Picasso加载图片时,Picasso 在没有从内存缓存中找到相应的缓存时,才会委托内部的OkHttpClient实例去下载图片。而默认的实例是始终从服务器加载图片的,作为默认的OkHttpClient 方法上是没有为响应缓存配置文件系统的。
指定自己的OkHttpClient实例允许响应是从文件系统返回一个缓存。没有图片是从服务器加载的。特别重要的是,从app第一次启动之后。在这个时候,Picasso的内存缓存是无作用的,所以它会频繁的委托OkHttpClient实例去加载图片。
这就需要建立一个Picasso实例去配置你的OkHttpClient.如果你加载图片的代码是这样用的Picasso.with(context).load(…) .,这用的Picasso的单实例,就是懒懒的实例化和配置自己的OkHttpClient的方法with。因此,我们必须先分配自己的Picasso单例实例,再去with。要做到这点,只是把OkHttpClient实例再OkHttpDownLoader,和过你的Picasso.Builder实例的downloader方法:
final Picasso picasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(okHttpClient))
.build();
// The client should inject this instance whenever it is needed, but replace the singleton
// instance just in case.
Picasso.setSingletonInstance(picasso);
用你的OkHttpClient实例与 Retrofit1.9.x里的RestAdapter,把OkHttpClient实例封装在OkClient实例里面,并将其传递给你RestAdapter.Builder实例的setClient方法:
restAdapterBuilder.setClient(new OkClient(httpClient));
在 Retrofit2.0里,只是简单的传递OkHttpClient实例到你的Retrofit.Builder实例的client方法。
在可汗学院的app中,我们利用 Dagger来确保只有一个OkHttpClient实例,并且同时被Picasso和Retrofit使用。我们为OkHttpClient实例创建了一个Provider 关于@Singleton的标注:
@Provides
@Singleton
public OkHttpClient okHttpClient(final Context context, ...) {
final OkHttpClient okHttpClient = new OkHttpClient();
configureClient(okHttpClient, ...);
return okHttpClient;
}
这个OkHttpClient是使用Dagger的注入,进创建RestAdapter和Picasso实例的其它providers.
当客户端在每一个请求中提供了详细的User-Agent header值,日志文件和分析会非常庞大的信息量。默认情况下,OkHttp会有一个指定的User-Agent值(只有OkHttp的版本号)。而要指定自己的用户代理,首先创建一个拦截器取代那个值,这里采用StackOverflow上的做法:
public final class UserAgentInterceptor implements Interceptor {
private static final String USER_AGENT_HEADER_NAME = "User-Agent";
private final String userAgentHeaderValue;
public UserAgentInterceptor(String userAgentHeaderValue) {
this.userAgentHeaderValue = Preconditions.checkNotNull(userAgentHeaderValue);
}
@Override
public Response intercept(Chain chain) throws IOException {
final Request originalRequest = chain.request();
final Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader(USER_AGENT_HEADER_NAME)
.addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)
.build();
return chain.proceed(requestWithUserAgent);
}
}
构建User-Agent header 值传入UserAgentInterceptor的构造器里,使用任何值你都可以发现信息。我们使用:
• an os value of Android to clearly communicate that this is an Android device 一个Android系统的值,以清晰的表明,这是一个Android设备
• Build.MODEL, or the “end-user-visible name for the end product” 或者“最终用户可见的名字为终端产品“
• Build.BRAND, or the “consumer-visible brand with which the product/hardware will be associated” 或者“消费者可见的品牌与产品/硬件关联
• Build.VERSION.SDK_INT, or the “user-visible SDK version of the [Android] framework” 或者”用户可见的SDK版本的Android框架
• BuildConfig.APPLICATION_ID
• BuildConfig.VERSION_NAME
• BuildConfig.VERSION_CODE
最后三个的值由applicationid,cersioncode和versionName 值在Gradle创建脚本里指定。关于更多的信息,可以翻阅你app的版本信息文档,并在Gradle上配置你的applicationid。
注意,如果你的app用的是WebView, 你可以将其配置的User-Agent header值与UserAgentInterceptor构建的值一样:
WebSettings settings = webView.getSettings();
settings.setUserAgentString(userAgentHeaderValue);
在2.5.0版本之前,OkHttp 请求默认是不会超时的。从2.5.0版本开始,如果建立了连接,从连接中读取下一个字节,或写下一个字节的连接时间需要超过10秒完成,就请求超时。
这么做无非是更新了2.5.0版在代码上出现的bug,仅仅因为我们刚开始练习某些错误行为。
覆盖这些默认值,借助 setConnectTimeout,setReadTimeout或者setWriteTimeout 区别。
注意:Picasso 和Retrofit为它们的默认OkHttpClient实例指定了不同的请求超时时间。
默认情况下,Picasso 的指定值:
A connect timeout of 15 seconds.
A read timeout of 20 seconds.
A write timeout of 20 seconds.
而Retrofit的指定设置:
A connect timeout of 15 seconds.
A read timeout of 20 seconds.
No write timeout.
通过配置Picasso和Retrofit与你的OkHttpClient实例,你可以确保所有的请求超时的设值一致。
再次,OkHttp的默认配置提供了很大的效率。通过以上步骤,你可以增加它的灵动性和内省的能力,并提高app的性能。