IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Retrofit配合RxJava实现网络请求 -> 正文阅读

[网络协议]Retrofit配合RxJava实现网络请求

前言

Retrofit是目前使用非常广泛的网络请求框架,它基于OkHttp实现,通过注解配置需求,使用简单方便,而且提供了RxJava支持,本篇文章将介绍Retrofit的一些简单用法,以及和RxJava配合使用来实现网络请求的过程。

导入依赖

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
//RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'

配置Retrofit

Retrofit的创建过程如下

val builder = Retrofit.Builder()
    .baseUrl(baseUrl)
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(factory)

下面简单说明一下Retrofit.Builder的几个方法:

baseUrl:配置请求的URL;

addCallAdapterFactory:添加CallAdapter,常用的如DefaultCallAdapterFactory支持Call类型的调用方式,而RxJava2CallAdapterFactory支持Observable类型的调用方式,也就是支持RxJava调用;

addConverterFactory:添加数据类型转换器,将后台返回的数据直接转换成实体对象,比如常用的GsonConverterFactory.create()就是用来支持gson解析,当然,我们也可以自定义转换器将返回的数据重新组装,转换成指定的实体类;

client:配置OkHttpClient,可以看到Retrofit的底层实现就是OkHttp,配置OkHttpClient时可以设置接口超时时长,管理cookie,配置缓存,添加拦截器等等;

addInterceptor:添加拦截器,拦截器的用处非常广泛,比如将后台返回的数据转换成实体类也可以通过拦截器来实现,拦截器还可以用来缓存get请求结果,打印后台返回日志的,后面将介绍几个实用的自定义拦截器;

完整代码:

class RetrofitFactory {

    companion object {
        //单例对象
        val instance = SingleTonHolder.holder
    }

    //静态内部类单例模式
    private object SingleTonHolder {
        val holder = RetrofitFactory()
    }

    fun <T> createService(
        clazz: Class<T>,
        baseUrl: String,
        factory: Converter.Factory = GsonConverterFactory.create(),
        interceptor: Interceptor? = null,
        timeout: Long = 30L,    //默认超时时长
        cache: Boolean = false  //是否进行缓存
    ): T {
        val builder = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(factory)
        if (interceptor != null) {
            builder.client(getOkHttpBuilder(timeout, cache).addInterceptor(interceptor).build())
        } else {
            builder.client(getOkHttpBuilder(timeout, cache).build())
        }
        return builder.build().create(clazz)
    }

    /**
     * 设置OkHttpClient.Builder
     *
     * @param timeout 接口超时时长
     * @param cache   是否进行缓存
     */
    private fun getOkHttpBuilder(timeout: Long, cache: Boolean): OkHttpClient.Builder {
        //添加一个log拦截器,打印所有的log
//        val httpLoggingInterceptor = HttpLoggingInterceptor()
        val httpLoggingInterceptor = HttpLoggingInterceptor(HttpLogInterceptor())
        //可以设置请求过滤的水平,body,basic,headers
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        val builder = OkHttpClient.Builder()
            .connectTimeout(timeout, TimeUnit.SECONDS)
            .readTimeout(timeout, TimeUnit.SECONDS)
//            .retryOnConnectionFailure(true)  //设置出现错误进行重新连接
            .cookieJar(CookieJarManage.instance)  //cookie持久化
        if (cache) {
            val cacheDir = File(BaseApplication.instance.cacheDir, "httpCache")  //设置缓存路径
            val cacheSize: Long = 10 * 1024 * 1024  //设置缓存大小为10M
            builder.cache(Cache(cacheDir, cacheSize))
                .addInterceptor(HttpCacheInterceptor())
        }
        if (Config.DEBUG) {
            builder.sslSocketFactory(SSLSocketFactoryUtil.getPassAnySSLSocketFactory())  //不校验证书
                .hostnameVerifier { p0, p1 -> true }  //不校验服务器返回的信息
                .addInterceptor(httpLoggingInterceptor) //打印日志,以便调试
        }
        return builder
    }

    //清除缓存
    fun clearCache(): Boolean {
        val cacheDir = File(BaseApplication.instance.cacheDir, "httpCache")
        return FileUtil.deleteDirectory(cacheDir)
    }

}

OkHttpClient的几个方法:

cookieJar:管理cookie

class CookieJarManage : CookieJar {

    companion object {
        //单例对象
        val instance = SingleTonHolder.holder
    }

    //静态内部类单例模式
    private object SingleTonHolder {
        val holder = CookieJarManage()
    }

    private val cookieStore = HashMap<String, MutableList<Cookie>>()

    //网路访问后将服务器返回的cookies和对应的url存储在cookieStore中
    override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
        cookieStore.put(url.host(), cookies)
    }

    //网路访问开始的时候,根据访问的url去查找cookie,然后将cokies放到请求头里面
    override fun loadForRequest(url: HttpUrl): MutableList<Cookie> =
        cookieStore.get(url.host()) ?: mutableListOf<Cookie>() //cookieStore.get(url)为null时返回mutableListOf<Cookie>()

}

sslSocketFactory:校验证书,这里选择不校验证书,代码如下:

object SSLSocketFactoryUtil {

    fun getPassAnySSLSocketFactory(): SSLSocketFactory {
        val sslContext = SSLContext.getInstance("TLS")
        return sslContext.apply {
            init(null, arrayOf<TrustManager>(TrustAllManager()), SecureRandom())
        }.socketFactory
    }

    private class TrustAllManager : X509TrustManager {
        override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {
        }

        override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {
        }

        override fun getAcceptedIssuers(): Array<X509Certificate> {
            return emptyArray<X509Certificate>()
        }

    }

}

addInterceptor:添加拦截器,上面的诸如HttpLogInterceptor、HttpCacheInterceptor等自定义拦截器将在下面介绍。

自定义拦截器

日志拦截器HttpLogInterceptor

构建HttpLoggingInterceptor传入自定义拦截器,自定义服务器请求报文和响应报文输出格式,比如将输出日志json格式化,代码如下

class HttpLogInterceptor : HttpLoggingInterceptor.Logger {

    companion object {
        const val TAG = "HttpLogger"
    }

    private val mMessage = StringBuilder()

    override fun log(message: String) {
        var message = message
        // 请求或者响应开始
        if (message.startsWith("--> POST")) {
            mMessage.setLength(0)
        }
        // 以{}或者[]形式的说明是响应结果的json数据,需要进行格式化
        if (message.startsWith("{") && message.endsWith("}")
            || message.startsWith("[") && message.endsWith("]")
        ) {
            message = formatJson(message)
        }
        mMessage.append(message)
        mMessage.append("\n")
        // 响应结束,打印整条日志
        if (message.startsWith("<-- END HTTP")) {
            val spacingFlag =
                "=============================================================================================\n"
            val content = " \n$spacingFlag$mMessage$spacingFlag"
            printLog(TAG, content)
        }
    }

    private fun printLog(tag: String, msg: String) {  //信息太长,分段打印
        var msg = msg
        //因为String的length是字符数量不是字节数量所以为了防止中文字符过多,
        //把4*1024的MAX字节打印长度改为2001字符数
        val max_str_length = 2001 - tag.length
        //大于4000时
        while (msg.length > max_str_length) {
            Log.i(tag, msg.substring(0, max_str_length))
            msg = msg.substring(max_str_length)
        }
        //剩余部分
        Log.i(tag, msg)
    }

    //格式化Json字符串
    private fun formatJson(strJson: String): String {
        // 计数tab的个数
        var tabNum = 0
        val jsonFormat = StringBuilder()
        val length = strJson.length

        var last: Char = 0.toChar()
        for (i in 0 until length) {
            val c = strJson[i]
            if (c == '{') {
                tabNum++
                jsonFormat.append(c + "\n")
                jsonFormat.append(getSpaceOrTab(tabNum))
            } else if (c == '}') {
                tabNum--
                jsonFormat.append("\n")
                jsonFormat.append(getSpaceOrTab(tabNum))
                jsonFormat.append(c)
            } else if (c == ',') {
                jsonFormat.append(c + "\n")
                jsonFormat.append(getSpaceOrTab(tabNum))
            } else if (c == ':') {
                jsonFormat.append("$c ")
            } else if (c == '[') {
                tabNum++
                val next = strJson[i + 1]
                if (next == ']') {
                    jsonFormat.append(c)
                } else {
                    jsonFormat.append(c + "\n")
                    jsonFormat.append(getSpaceOrTab(tabNum))
                }
            } else if (c == ']') {
                tabNum--
                if (last == '[') {
                    jsonFormat.append(c)
                } else {
                    jsonFormat.append("\n" + getSpaceOrTab(tabNum) + c)
                }
            } else {
                jsonFormat.append(c)
            }
            last = c
        }
        return jsonFormat.toString()
    }

    private fun getSpaceOrTab(tabNum: Int): String {
        val sbTab = StringBuilder()
        for (i in 0 until tabNum) {
            sbTab.append('\t')
        }
        return sbTab.toString()
    }

}

现在控制台输出日志格式如下,相对来说要清晰醒目的多

    =============================================================================================
    --> GET http://wthrcdn.etouch.cn/weather_mini?city=%E5%8C%97%E4%BA%AC
    --> END GET
    <-- 200 OK http://wthrcdn.etouch.cn/weather_mini?city=%E5%8C%97%E4%BA%AC (119ms)
    Server: Tengine/2.3.2
    Date: Sat, 13 Nov 2021 12:39:34 GMT
    Connection: keep-alive
    Access-Control-Allow-Headers: *
    Access-Control-Allow-Methods: *
    Access-Control-Allow-Origin: *
    Cache-Control: must-revalidate, max-age=300
    Age: 0
    X-Via-Ucdn: HIT by 36.158.229.109, HIT by 180.97.190.59
    
    {
    	"data": {
    		"yesterday": {
    			"date": "12日星期五",
    			"high": "高温 15℃",
    			"fx": "西北风",
    			"low": "低温 0℃",
    			"fl": "<![
    				CDATA[
    					2级
    				]
    			]>",
    			"type": "晴"
    		},
    		"city": "北京",
    		"forecast": [
    			{
    				"date": "13日星期六",
    				"high": "高温 18℃",
    				"fengli": "<![
    					CDATA[
    						2级
    					]
    				]>",
    				"low": "低温 1℃",
    				"fengxiang": "西北风",
    				"type": "晴"
    			},
    			{
    				"date": "14日星期天",
    				"high": "高温 17℃",
    				"fengli": "<![
    					CDATA[
    						2级
    					]
    				]>",
    				"low": "低温 2℃",
    				"fengxiang": "西北风",
    				"type": "晴"
    			},
    			{
    				"date": "15日星期一",
    				"high": "高温 15℃",
    				"fengli": "<![
    					CDATA[
    						1级
    					]
    				]>",
    				"low": "低温 -1℃",
    				"fengxiang": "东北风",
    				"type": "晴"
    			},
    			{
    				"date": "16日星期二",
    				"high": "高温 16℃",
    				"fengli": "<![
    					CDATA[
    						1级
    					]
    				]>",
    				"low": "低温 -1℃",
    				"fengxiang": "西南风",
    				"type": "晴"
    			},
    			{
    				"date": "17日星期三",
    				"high": "高温 11℃",
    				"fengli": "<![
    					CDATA[
    						1级
    					]
    				]>",
    				"low": "低温 0℃",
    				"fengxiang": "西风",
    				"type": "多云"
    			}
    		],
    		"ganmao": "感冒易发期,外出请适当调整衣物,注意补充水分。",
    		"wendu": "14"
    	},
    	"status": 1000,
    	"desc": "OK"
    }
    <-- END HTTP (960-byte body)
    =============================================================================================

网络缓存拦截器HttpCacheInterceptor

如果希望能够缓存接口请求结果,使得在无网络连接的情况下请求接口地址也能返回数据,也可以通过自定义拦截器来实现,原理就是使用maxAge设置在线缓存,maxStale设置离线缓存,需要注意的是这种方式只支持缓存GET请求结果,并不支持POST请求方式,代码如下:

class HttpCacheInterceptor(
    private val maxAge: Int = 0,  //缓存过期时间,单位是秒,默认不使用缓存
    private val maxStale: Int = 3 * 24 * 60 * 60  //缓存过期时间,在请求头设置有效,在响应头设置无效,默认缓存3天
) : Interceptor {

    companion object {
        private const val TAG = "HttpCacheInterceptor"
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        val isConnected = NetworkUtil.isConnected()
        LogUtil.w(TAG, "网络是否连接:${isConnected}")
        if (!isConnected) {  //无网络的情况
            LogUtil.w(TAG, "从缓存获取数据,max-stale=$maxStale")
            val control = CacheControl.Builder()
                .onlyIfCached()
                .maxStale(maxStale, TimeUnit.SECONDS)
                .build()
            request = request.newBuilder()
                .cacheControl(control)  //从缓存读取
                .build()
        }
        var response = chain.proceed(request)
        if (isConnected) {  //有网络的情况
            LogUtil.w(TAG, "max-age=$maxAge")
            response = response.newBuilder()
                .removeHeader("Pragma")
                .header("Cache-Control", "public, max-age=" + maxAge)  //如果想要不缓存,maxAge直接设置为0
                .build()
        }
        return response
    }
}

上面的拦截器默认有网络时不使用缓存,也就是说有网络情况下每次请求都是实时请求的结果,无网络时使用缓存,缓存过期时间为3天。如果需要在有网络时也使用缓存避免频繁的接口请求,可设置maxAge不为0,如设置为5则意味着缓存过期时间为5秒。

限制最多重试次数的拦截器MaxRetryInterceptor

限制最多请求接口次数,代码如下:

class MaxRetryInterceptor(private val maxRetryCount: Int) : Interceptor {

    companion object {
        private const val TAG = "MaxRetryInterceptor"
    }

    private var retryCount = 1

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        LogUtil.w(TAG, "retry count:$retryCount")
        var response = chain.proceed(request)  //默认请求1次
        while (!response.isSuccessful && retryCount < maxRetryCount) {
            retryCount++
            LogUtil.w(TAG, "retry count:$retryCount")
            response = chain.proceed(request)  //重试
        }
        return response
    }
}

使用

GET请求

进行GET请求的参数类注解有@Query @QueryMap @QueryName等,下面使用一个天气接口进行GET请求:

--> GET http://wthrcdn.etouch.cn/weather_mini?city=北京
--> END GET

@Query

定义接口地址:

//获取天气信息,@Query,GET请求
@GET("weather_mini")
fun getWeatherByQuery(@Query("city") city: String): Observable<WeatherBean>

配置参数及使用:

//获取天气信息,@Query,GET请求
private fun getWeatherByQuery(city: String) {
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.WEATHER_URL)
        .getWeatherByQuery(city)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(WeatherObserver())
}

相关类介绍,实体类定义如下:

data class WeatherBean(
    val status: Int,
    val desc: String,
    val data: DataBean?
) : Serializable {

    data class DataBean(
        val yesterday: YesterdayBean,
        val city: String,
        val forecast: List<ForecastBean>,
        val ganmao: String,
        val wendu: String
    ) : Serializable {

        data class YesterdayBean(
            val date: String,
            val high: String,
            val fx: String,
            val low: String,
            val fl: String,
            val type: String
        ) : Serializable

        data class ForecastBean(
            val date: String,
            val high: String,
            val fengli: String,
            val low: String,
            val fengxiang: String,
            val type: String
        ) : Serializable

    }

    fun isSuccess(): Boolean = status == 1000
}

UrlConstant定义如下:

object UrlConstant {
    const val BAIDU_URL = "https://www.baidu.com/"
    const val WEATHER_URL = "http://wthrcdn.etouch.cn/"
    const val NEWS_URL = "https://api.apiopen.top/"
}

SchedulerUtil简化了在子线程请求网络,然后切换到主线程执行的过程:

object SchedulerUtil {

    fun <T> ioToMain(): ObservableTransformer<T, T> {
        return ObservableTransformer { observable ->
            observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
        }
    }

}

再看一下WeatherObserver,它返回了一个Observer,即RxJava中的观察者:

private fun WeatherObserver(): Observer<WeatherBean> {
    return object : Observer<WeatherBean> {
        override fun onComplete() {
        }
        override fun onSubscribe(d: Disposable) {
        }
        override fun onNext(bean: WeatherBean) {
            if (bean.isSuccess()) {
                val result = JsonUtil.formatJson(Gson().toJson(bean))
                alert(result)
            } else {
                showToast(bean.desc)
            }
        }
        override fun onError(e: Throwable) {
            showToast(ExceptionUtil.convertExceptopn(e))
            e.printStackTrace()
        }
    }
}

@QueryMap

定义接口地址:

//获取天气信息,@QueryMap,GET请求
@GET("weather_mini")
fun getWeatherByQueryMap(@QueryMap map: HashMap<String, Any>): Observable<WeatherBean>

配置参数及使用:

//获取天气信息,@QueryMap,GET请求
private fun getWeatherByQueryMap(city: String) {
    val map = HashMap<String, Any>()
    map.put("city", city)
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.WEATHER_URL)
        .getWeatherByQueryMap(map)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(WeatherObserver())
}

可以看到,QueryMap可以将多个Query参数直接封装成一个HashMap传入,但是最后结果和Query是一样的。

无Query的情况

比如只是简单的访问https://www.baidu.com/

定义接口:

//简单的访问IP地址,Retrofit + RxJava
@GET("/")
fun accessUrlRxJava(): Observable<ResponseBody>

调用,传入地址即可:

//访问网址,Retrofit + RxJava
private fun accessUrlRxJava(url: String) {
    RetrofitFactory.instance.createService(ApiService::class.java, url)
        .accessUrlRxJava()
        .compose(SchedulerUtil.ioToMain())
        //.subscribeOn(Schedulers.io())
        //.observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : Observer<ResponseBody> {
            override fun onComplete() {
            }
            override fun onSubscribe(d: Disposable) {
            }
            override fun onNext(response: ResponseBody) {
                showToast("访问成功!")
                alert(response.string())
            }
            override fun onError(e: Throwable) {
                showToast(ExceptionUtil.convertExceptopn(e))
                e.printStackTrace()
            }
        })
}

POST请求

进行POST请求的参数类注解有@Field @FieldMap @Body等,以请求网易新闻列表为例:

--> POST https://api.apiopen.top/getWangYiNews
Content-Type: application/x-www-form-urlencoded
Content-Length: 14
count=2&page=1
--> END POST (14-byte body)

@Field

定义接口地址:

//获取网易新闻,@Field,POST请求
@FormUrlEncoded
@POST("getWangYiNews")
fun getWangYiNewsByField(@Field("page") page: String, @Field("count") count: String): Observable<NewsListBean>

配置参数及使用:

//获取网易新闻,@Field,POST请求
private fun getWangYiNewsByField(page: String, count: String) {
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.NEWS_URL)
        .getWangYiNewsByField(page, count)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(NewsObserver())
}

相关类介绍,实体类定义如下:

data class NewsListBean(
    val code: Int,
    val message: String,
    val result: MutableList<ResultBean>?
) : Serializable {
    data class ResultBean(
        val path: String,
        val image: String,
        val title: String,
        val passtime: String
    ) : Serializable

    fun isSuccess(): Boolean = code == 200
}

NewsObserver代码如下:

private fun NewsObserver(): Observer<NewsListBean> {
    return object : Observer<NewsListBean> {
        override fun onComplete() {
        }
        override fun onSubscribe(d: Disposable) {
        }
        override fun onNext(bean: NewsListBean) {
            if (bean.isSuccess()) {
                val result = JsonUtil.formatJson(Gson().toJson(bean))
                alert(result)
            } else {
                showToast(bean.message)
            }
        }
        override fun onError(e: Throwable) {
            showToast(ExceptionUtil.convertExceptopn(e))
            e.printStackTrace()
        }
    }
}

@FieldMap

定义接口地址:

//获取网易新闻,@FieldMap,POST请求
@FormUrlEncoded
@POST("getWangYiNews")
fun getWangYiNewsByFieldMap(@FieldMap map: HashMap<String, Any>): Observable<NewsListBean>

配置参数及使用:

//获取网易新闻,@FieldMap,POST请求
private fun getWangYiNewsByFieldMap(page: String, count: String) {
    val map = HashMap<String, Any>()
    map.put("page", page)
    map.put("count", count)
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.NEWS_URL)
        .getWangYiNewsByFieldMap(map)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(NewsObserver())
}

@Body

定义接口地址:

//获取网易新闻,@Body,POST请求
@POST("getWangYiNews")
fun getWangYiNewsByBody(@Body requestBody: RequestBody): Observable<NewsListBean>

配置参数及使用:

//获取网易新闻,@Body,POST请求
private fun getWangYiNewsByBody(page: String, count: String) {
    val map = HashMap<String, Any>()
    map.put("page", page)
    map.put("count", count)
    val sb = StringBuilder()
    map.forEach {
        sb.append("${it.key}=${it.value}&")
    }
    val data = sb.toString().substring(0, sb.toString().length - 1)
    //val data = "page=$page&count=$count"
    val body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), data)
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.NEWS_URL)
        .getWangYiNewsByBody(body)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(NewsObserver())
}

@Body是以表单形式提交POST请求

缓存GET请求

//测试缓存机制
private fun testCache(city: String) {
    RetrofitFactory.instance.createService(ApiService::class.java, UrlConstant.WEATHER_URL, cache = true)
        .getWeatherByQuery(city)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(WeatherObserver())
}

RetrofitFactory相关缓存设置代码:

/**
 * 设置OkHttpClient.Builder
 *
 * @param timeout 接口超时时长
 * @param cache   是否进行缓存
 */
private fun getOkHttpBuilder(timeout: Long, cache: Boolean): OkHttpClient.Builder {
    //添加一个log拦截器,打印所有的log
      val httpLoggingInterceptor = HttpLoggingInterceptor()
    val httpLoggingInterceptor = HttpLoggingInterceptor(HttpLogInterceptor())
    //可以设置请求过滤的水平,body,basic,headers
    httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
    val builder = OkHttpClient.Builder()
        .connectTimeout(timeout, TimeUnit.SECONDS)
        .readTimeout(timeout, TimeUnit.SECONDS)
          .retryOnConnectionFailure(true)  //设置出现错误进行重新连接
        .cookieJar(CookieJarManage.instance)  //cookie持久化
    if (cache) {
        val cacheDir = File(BaseApplication.instance.cacheDir, "httpCache")  //设置缓存路径
        val cacheSize: Long = 10 * 1024 * 1024  //设置缓存大小为10M
        builder.cache(Cache(cacheDir, cacheSize))
            .addInterceptor(HttpCacheInterceptor())
    }
    if (Config.DEBUG) {
        builder.sslSocketFactory(SSLSocketFactoryUtil.getPassAnySSLSocketFactory())  //不校验证书
            .hostnameVerifier { p0, p1 -> true }  //不校验服务器返回的信息
            .addInterceptor(httpLoggingInterceptor) //打印日志,以便调试
    }
    return builder
}
//清除缓存
fun clearCache(): Boolean {
    val cacheDir = File(BaseApplication.instance.cacheDir, "httpCache")
    return FileUtil.deleteDirectory(cacheDir)
}

设置最多尝试请求3次

//最多重试3次
private fun testMaxRetry(city: String) {
    RetrofitFactory.instance.createService(
        ApiService::class.java,
        UrlConstant.WEATHER_URL,
        interceptor = MaxRetryInterceptor(3)
    ).getWeatherByQuery(city)
        .compose(SchedulerUtil.ioToMain())
        .subscribe(WeatherObserver())
}

源码地址

不得不说,Retrofit是一个非常好用的网络请求框架,而RxJava也是非常强大的,本文只是介绍了Retrofit和RxJava一些基础的用法,还有很多实用的方法需要去系统的学习!这里就不一一介绍了。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 16:11:28  更:2021-11-15 16:13:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/3 21:15:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码