初始化 - GateOfTruth/OkSimple GitHub Wiki

全局初始化

oksimple 没有对okhttpclient进行任何的封装,我只创建了一个默认的对象,就像这样

var okHttpClient = OkHttpClient()

所以如果你对okhttpclient有什么特殊处理,诸如connectTimeout,protocols,cookiejar,eventlistener,拦截器等,建议在application中对其进行初始化,就像这样

OkSimple.okHttpClient=OkHttpClient.Builder().addInterceptor(logInterceptor)
            .connectTimeout(100,TimeUnit.SECONDS)
            .writeTimeout(100,TimeUnit.SECONDS)
            .readTimeout(100,TimeUnit.SECONDS)
            .cache(Cache(cacheDir,1024*1024*10))
            .cookieJar(object :CookieJar{
                override fun loadForRequest(url: HttpUrl): List<Cookie> {
                    
                }

                override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
                    
                }

            })
            .eventListener(object :EventListener(){
                override fun dnsStart(call: Call, domainName: String) {
                    
                }
            })
            .build()

缓存

关于缓存,okhttp框架本身就对缓存做了相应处理,只需要后端返回正确的header,okhttp的缓存就可以正常工作。因此,我没有像其他一些框架一样,重新写一遍缓存。对此有疑问的可以看一下我翻译的这篇文章

无网络启用okhttp FORCE_CACHE

oksimple默认会在没有网络的情况下,添加force cache。也就是说你可以不用再写这样的拦截器了,因为这个功能已经实现了。

//不需要再写这样的拦截器,这个功能已经内置了
public class ForceCacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder builder = chain.request().newBuilder();
        if (!NetworkUtils.internetAvailable()) {
            builder.cacheControl(CacheControl.FORCE_CACHE);
        }
        
        return chain.proceed(builder.build());
    }
}
okHttpClient.addInterceptor(new ForceCacheInterceptor());

要开启这个功能,需要在你的application里初始化oksimple用到的application

OkSimple.application=this//this指代你的application

默认添加force cache这个功能也是可以控制的

OkSimple.networkUnavailableForceCache=true//默认为true,当为false时,则不会在断网的时候添加force cache。

如果你一开始就不打算使用这个功能,那么你就不要初始化oksimple用到的application。也就是说要开启在断网情况下添加force cache这个功能,需要满足两个条件,第一个是给oksimple用到的application赋值,第二个是networkUnavailableForceCache为true(虽然它本身默认就是true)。

全局参数

全局参数的话,诸如各种渠道参数等,或者全局的header,诸如token等,可以分别调用

OkSimple.addGlobalParams("key","value")

OkSimple.addGlobalHeader("key","value")

你可以在任何地方调用这两个方法,都会生效,如果你想对这些参数进行一些操作,那么直接获取OkSimple.globalHeaderMap和OkSimple.globalParamsMap即可。

取消请求

//取消某个请求
OkSimple.cancelCall(tag)
//取消所有请求
OkSimple.cancelAll()

取消单个请求需要传入当前请求的tag。tag是有默认值的,tag的默认值等于当前请求的url。需要注意的是,当前url是.get或.post等方法里的url,也就是说,不包括params()方法和addGlobalParams()方法添加的参数。之后在其他地方提到的url都是如此,就不再赘述。你也可以调用settag方法,配置自定义tag。⚠️注意:tag一经设置之后,请勿通过策略系统里的requestbuilder重新设置,否则可能会导致无法取消,从而引起内存泄漏。同时oksimple也会保证请求期间tag不变。举个例子:你的一个请求没有手动设置tag,那么他的默认tag就是.get或.post等方法里的url,但是之后你通过策略系统修改了域名或者参数,tag也依然会是一开始请求的域名和参数。

防止连续的重复请求

Oksimple默认会防止重复请求,也就是当你向服务器发出一个请求的时候,如果服务器没有返回结果,无论这个结果是成功,失败或者超时,都会对之后的同一个请求进行拦截,之后的请求,不会发送到服务器,直到当前请求有了返回。而判断是否是同一个请求,是根据tag(tag的默认值是url),参数字符串,和请求长度来综合判断来进行判断的。防止重复请求的开关是

OkSimple.preventContinuousRequests=false//默认为false,当为true时,则会防止重复请求

当开关开启的时候,是可以有效防止诸如用户狂点按钮,导致短时间发送多个请求的情况发生的。这个功能的逻辑是相同的请求同时发起的时候,多余的请求会被拦截,直到上一个请求成功或者失败。
被拦截的请求,如果是异步请求,会回调ResultCallBack的requestAbandon方法,并在会在控制台打印error级别的log。当然,你也可以对这个方法进行重写。如果是同步请求,会在BaseSynchronizeBeanexception里看到一个RuntimeException,描述也是:Same Request!!! This request has been abandoned!!!
注意事项: 除了post和get请求,其他的请求,诸如HEAD,PUT,DELETE等,即使把preventContinuousRequests设置为true也不会进行重复请求的判断和拦截。主要是这几个请求没有进行单独处理,本着宁愿多请求也不漏请求的原则,所以这几种类型的请求,是不会被拦截的。

BrotliInterceptor

根据change log,从okhttp 4.1开始,okhttp开始支持Brotli压缩。关于Brotli和gzip的区别,可以去看这篇文章,大致就是说Brotli改变了编码方式,可以相比gzip达到更高的压缩率,减少37%的延迟。要用的话需要单独导入

implementation 'com.squareup.okhttp3:okhttp-brotli:4.x'//和okhttp版本相同

然后在代码里这样使用

OkHttpClient.Builder().addInterceptor(BrotliInterceptor)

注意BrotliInterceptor是单例模式的,在java里也按单例的用法使用就行了。有人会问,为啥是addInterceptor而不是addNetworkInterceptor,这里我放部分源码,你应该就理解了

//这是okhttp RealCall的部分源码
 val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors
    interceptors += RetryAndFollowUpInterceptor(client)
    interceptors += BridgeInterceptor(client.cookieJar)
    interceptors += CacheInterceptor(client.cache)
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)
//这是BridgeInterceptor里的源码
  var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true
      requestBuilder.header("Accept-Encoding", "gzip")
    }
//这是BrotliInterceptor的源码
 if (chain.request().header("Accept-Encoding") == null) {
        val request = chain.request().newBuilder()
            .header("Accept-Encoding", "br,gzip")
            .build()

        val response = chain.proceed(request)

        uncompress(response)
      } else {
        chain.proceed(chain.request())
      }

所以原因就恨明显了,okhttp会先走BridgeInterceptor导致你的Accept-Encoding不为null,如果你放在networkInterceptors的话,BrotliInterceptor发现你已经有了Accept-Encoding,则不会在Accept-Encoding里添加br。添加br的意思就是像服务器请求使用Brotli压缩。

但是不管是gzip还是brotli,都是需要发送和接收的双方都能够压缩和解压。android手机既可以是发送方也可以是接收方,服务器同理。假如其中一方不支持Brotli压缩的话,则一切也就无从谈起了。

所以我们可以看到BrotliInterceptor在Accept-Encoding放了两个字段,当服务器端不支持Brotli压缩的时候,可以使用gzip压缩。目前来说,支持Brotli压缩的网站还是比较少的。

代码里有个BrotliActivity,是示范用,表示原本不使用br压缩的,可以在某一时刻开启br压缩。

⚠️ **GitHub.com Fallback** ⚠️