Android-Retrofit

Retrofit的定位

Retrofit并不是作为一个网络请求库存在的,而是作为一个网络请求适配层,就比如网络请求库如果是jdbc,那么MyBatis就是Retrofit。Retrofit2底层默认使用OkHttp作为其网络请求库,而其本身只是对网络请求过程的一般操作做了封装,例如参数的构建,数据的解析以及线程切换。

Retrofit秉承Restful风格的简洁性,将每个网络请求定义为java接口的一个方法,定义路径可以由方法的注解提供,header和请求参数可以由方法的参数提供,返回一个Retrofit的Call对象,这种Call对接类似OkHttp的Call,可以有execute和enqueue两种方式执行网络请求,分别是同步和异步。

Retrofit Demo

还是使用上篇OkHttp3的demo,只不过把样式定义抽取出来作为公共函数,只把网络请求暴露出来。

package com.benson.android.network

import android.content.Context
import android.graphics.Color
import android.view.Gravity
import android.view.ViewGroup
import android.widget.*

fun initView(context: Context, search: (TextView, String) -> Unit): ViewGroup {
    val content = LinearLayout(context)
    content.orientation = LinearLayout.VERTICAL
    val searchBar = LinearLayout(context)
    searchBar.orientation = LinearLayout.HORIZONTAL
    searchBar.gravity = gravity.center is For Sale
    val input = EditText(context)
    input.textSize = DisplayUtil.sp2px(context, 10.0F) * 1.0F
    searchBar.addView(input, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 7.0F))
    val searchBtn = Button(context)
    searchBtn.text = "search"
    searchBtn.textSize = DisplayUtil.sp2px(context, 10.0F) * 1.0F
    searchBar.addView(searchBtn, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 2.0F))
    content.addView(searchBar, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0F))
    val result = TextView(context)
    result.setBackgroundColor(color.black - 这个网站可出售。 - 最佳的color 来源和相关信息。)
    result.setTextColor(Color.WHITE)
    result.textSize = DisplayUtil.sp2px(context, 15.0F) * 1.0F
    val scrollView = ScrollView(context)
    scrollView.addView(result)
    content.addView(scrollView, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 10.0F))
    searchBtn.setOnClickListener {
        search(result, input.text.toString())
    }
    return content
}
```
这里还是用OkHttp的基本配置,并且使用异步回调
```
package com.benson.android.network

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import okhttp3.OkHttpClient
import retrofit2.*
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
import java.lang.StringBuilder
import java.util.concurrent.TimeUnit

class Retrofit2DemoActivity: AppCompatActivity() {

    companion object {
        const val BASE_URL = "https://api.github.com/users/"

        interface UserResource {
            @GET("{user}")
            fun getUser(@Path("user") user: String): Call<User>
        }
    }

    val client by lazy { OkHttpClient.Builder()
        .retryOnConnectionFailure(true) // 连接超时重试
        .connectTimeout(2L, TimeUnit.MINUTES) // 2s 连接超时
        .readTimeout(2L, TimeUnit.MINUTES) // 2s 读超时
        .writeTimeout(2L, TimeUnit.MINUTES) // 2s 写超时
        .build()
    }

    val retrofit by lazy { Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build()
    }

    val userResource by lazy {
        retrofit.create(UserResource::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(initView(this) { result, user ->
            userResource.getUser(user).enqueue(object: Callback<User> {
                override fun onFailure(call: Call<User>, t: Throwable) {
                    result.text = "query error with user=$user\n${t.message}"

                }

                override fun onResponse(call: Call<User>, response: Response<User>) {
                    result.text = StringBuilder("code=${response.code()}").append("\n")
                        .append("message=").append(response.message()).append("\n")
                        .append("body=").append(response.body()).toString()
                }
            })
        })
    }

}

这里的实体类是用json转javabean自动生成的就不贴了


整个请求过程简单描述就是使用Retrofit.Builder构造出一个Retrofit对象,使用Retrofit对象create出我们需要使用的Restful接口对象。这个接口不需要我们自己来实现,Retrofit会自动帮我们来实现,请求时需要把对应的参数传给接口的方法就行,这样的网络请求就像调用了一个普通的java方法,而异步请求的回调是Retrofit帮我们自动切换到了main线程。


Retrofit源码解析

还是老套路,首先看构造过程,先是Retrofit对象的构造过程,OkHttpClient的构造过程就不再赘述了,Retrofit使用Builder,传入一系列的构造参数,最后调用build方法返回一个Retrofit对象。build方法的最后就是new了一个Retrofit对象,将callFactory、baseUrl、converterFactories、callAdapterFactories、callbackExecutor以及validateEagerly对象传入Retrofit的构造器。


  • callFactory: 表示网络请求器,真正执行网络请求的接口
  • baseUrl: 网络请求的url前缀,Retrofit认为同一个Retrofit表示同一个域名下的一系列请求
  • converterFactories:类型转换器工厂,生产Converter,Converter负责将java对象转化为网络请求request参数,也负责把response结果转换为java对象。
  • callAdapterFactories:call的适配器工厂,生产CallAdapter,CallAdapter主要负责将底层返回的Call类型转换为请求接口定义的返回类型。
  • callbackExecutor,异步回调的执行器,默认使用MainThreadExecutor,执行的runnable使用Handler(Looper.getMainLooper()).post。
  • validateEagerly:是否提前缓存RetrofitInterface中定义的方法到Retrofit中来


Retrofit对象使用create方法传入一个接口的Class创建出这个接口的代理对象。Retrofit默认使用了Java的Proxy来创建代理对象,代理对象的实现是创建ServiceMethod对象,并调用其adapt方法,也就是请求接口的方法被调用时,其实是调用的相应ServiceMethod的adapt方法。前面说到validateEagerly是是否提前缓存RetrofitInterface中定义的方法,create方法这里就使用了这个validateEagerly,如果validateEagerly为true,就提前将所有的ServiceMethod创建并缓存起来,而真正调用时通过loadServiceMethod获取ServiceMethod时,就可以从缓存中获取。

ServiceMethod的adapt方法直接调用了callAdapter.adapt,ServiceMethod的callAdapter是在其build的时候使用createCallAdapter方法创建出来的,而ServiceMethod的createCallAdapter方法则是获取请求接口方法的注解和return类型,使用retrofit对象的callAdapter方法创建。在Retrofit对象中,callAdapter直接调用了nextCallAdapter方法,使用callAdapterFactories来创建CallAdapter对象。这里的callAdapterFactories是build创建Retrofit对象时传入进来的,如果不传,也会add进platform.defaultCallAdapterFactory。defaultCallAdapterFactory方法中对传入参数callbackExecutor进行判空,当然,这里的callbackExecutor不为空,因为默认是在main线程中回调,因此callAdapterFactory默认使用ExecutorCallAdapterFactory。而ExecutorCallAdapterFactory的get方法中则创建了一个CallAdapter对象,其adapt方法则是返回创建的ExecutorCallbackCall对象,这个ExecutorCallback对象也就是我们在最外面使用userResource.getUser(user)返回的一个Call的真实对象。

这样真正的请求过程就落在了ExecutorCallAdapterFactory的enqueue方法里了,enqueue方法中只调用了delegate的enqueue方法,并在onReponse中调用传入的callback对象的onResponse,在onFailture中调用callback的onFailure,并且回调都被post到了callbackExecutor中,而这里的delegate对象是创建ExecutorCallback时传进来的OkHttpCall对象,在enqueue的实现中,则是使用了OkHttp的Call,调用其enqueue方法,并在enqueue方法的callback回调中,调用ExecutorCallback的相应回调,这样就完成了OkHttp的请求代理Retrofit的请求,如果要实现其他网络请求底层库来实现Retrofit的请求,也可以模仿OkHttp对Retrofit的代理实现。

由于OkHttp没有自带javabean解析,因此需要Retrofit来适配OkHttp请求结果到javabean的解析过程,在OkHttpCall的response回调中,使用了parseResponse,在其中调用了serviceMethod.toResponse创建出了javabean对象。而ServiceMethod的toResponse方法则是直接调用了responseConverter的convert方法,还记得在demo中创建Retrofit的时候convert是传的GsonConverterFactory,因此这里使用的converter的真实对象则是GsonConverterFactory的responseBodyBodyConverter返回的GsonResponseBodyConverter对象。其convert方法则是直接使用了JsonReader对象,调用read方法构造出的javabean对象。


总结

Retrofit并不是一个网络请求库,而是一个网络请求适配库,是将网络请求适配到应用开发的库,使得调用网络请求像调用一个普通的java方法一样方便,而且遵循了restful的接口风格。Retrofit对网络请求使用java的接口进行定义,接口的方法表示请求参数,请求的额外信息由注解信息表示。而这个java接口不需要我们开发来实现,而是Retrfit内部使用了java的动态代理Proxy框架实现的,在invoke方法中,使用了构造的ServiceMethod方法来完成Call的构建,而Call对象的真正执行execute和enqueue两个方法,又被代理给真正的网络请求库。

而对于网络请求参数或者网络请求结果如何与javabean进行转化的问题,Retrofit提供了Converter<F, T>接口,其只有一个方法convert,作用是把给定的类型F转化为给定义的类型T。

Retrofit现在变得越来越流行,大家都来越关心Retrofit甚至超过了OkHttp,这是因为大家的注意力在应用开发架构上,而应用开发的过程中,开发模式往往比底层实现更重要,这也是为什么Java语言对C/C++更流行的原因,Java底层全是C/C++,那么用C/C++开发不就好了吗?但问题恰恰就在于Java开发能够让开发者更方便地让注意力放在开发模式上,而不是底层实现上,正如Retrofit能够让开发者像调用普通java方法一样进行网络请求,而不用关心网络请求到底作了哪些操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值