使用 Retrofit+LiveData+Java 进行网络请求

之前一直在使用 Kotlin 写 Retrofit,一系列 api 封装太好了,我甚至不知道这堆语法糖和 api 展开后是什么。搜索资料和看了点 demo 后,写了一篇 Java 版本的 Retrofit+LiveData,现在才感觉自己略懂。看来 Kotlin First 还是得建立在完全理解原有的Java框架的基础上啊。

概览

Retrofit 和 LiveData 结合的一种实现方法

  1. 构建 Retrofit 对象时,添加一个转换适配器工厂 CallAdapterFactory

  2. CallAdapterFactory 生产的适配器 CallAdapter 需要重写 responseType()adapt() 两个方法

    • responseType()

      用于返回从GSON数据到JAVA对象的类型

    • adapt()

      该方法用于将 Retrofit 请求返回时的 Call 对象转换为需要的类,这里为我们自定义的 LiveData 对象,为了后续监听网络回调,这里 CallAdapterFactory 构建 CallAdapter 时需要传递当前 Call 实例

  3. 我们自定义的 LiveData 对象需要重写 onActive()

    • onActive()

    该方法在 LiveData 实例的 observer 由 0变1 时会调用,我们传递进来的 Call 在这里使用。

    由于网络线程在后台运行,此时应该对 Call 实例 enqueue 一个 Callback,Callback 重写的 onResponse() 方法中对 LiveData<T> 进行 postValue\<T>()

这样我们的 LiveData 在有 observer 后就能及时收到请求的回调并进行更新了

1. 添加适配器工厂

...
Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(new MyOkHttpClient())
    // add an adapterFactory here
    .addCallAdapterFactory(new LiveDataCallAdapterFactory()) 
    .addConverterFactory(GsonConverterFactory.create())
    .build();
...

2. 定义工厂类

public class LiveDataCallAdapterFactory extends CallAdapter.Factory {
    private static final String TAG = "LiveDataCallAdapterFact";
    @Nullable
    @Override
    public CallAdapter<?, ?> get(@NotNull Type returnType, @NotNull Annotation[] annotations, @NotNull Retrofit retrofit) {
        if (getRawType(returnType) != LiveData.class){
            return null;
        }
        Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
        Class<?> rawType = getRawType(observableType);
        Log.d(TAG, "get: rawType="+ rawType.getSimpleName());
        return new LiveDataCallAdapter<>(observableType);
    }
}

3. 定义Call转换适配器

public class LiveDataCallAdapter<T> implements CallAdapter<T, LiveData<T>> {
    private final Type mResponseType;

    public LiveDataCallAdapter(Type mResponseType) {
        this.mResponseType = mResponseType;
    }

    @NotNull
    @Override
    // 用于返回从GSON数据到JAVA对象的的类型
    public Type responseType() {
        return mResponseType;
    }

    @NotNull
    @Override
    public LiveData<T> adapt(@NotNull Call<T> call) {
        return new MyLiveData<>(call);
    }
}

4. 自定义LiveData

public class MyLiveData<T> extends LiveData<T> {
    private AtomicBoolean started = new AtomicBoolean(false);
    private final Call<T> call;

    public MyLiveData(Call<T> call) {
        this.call = call;
    }

    // 在 observer 由 0变1 时会调用
    @Override
    protected void onActive() {
        super.onActive();
        if (started.compareAndSet(false, true)){
            call.enqueue(new Callback<T>() {
                @Override
                public void onResponse(@NotNull Call<T> call, @NotNull Response<T> response) {
                    MyLiveData.super.postValue(response.body());
                }

                @Override
                public void onFailure(@NotNull Call<T> call, @NotNull Throwable t) {
                    MyLiveData.super.postValue(null);
                }
            });
        }
    }
}

完成上述四部分后,Retrofit就能正常响应以LiveData<T>为返回值的方法了

简易实例

一般而言,Retrofit 对象需要一个静态类进行全局管理,这里为了减少static的书写,使用 Kotlin 的object关键字声明静态类

Retrofit 管理类 Retrofit Manager

private const val TAG = "RetrofitManager"
object RetrofitManager {
    private var baseUrl:String = "https://www.wanandroid.com/user/"
    private var timeoutDuration = 10L
    private val retrofitMap = ConcurrentHashMap<String, Retrofit>()
    private var okHttpBuilder = OkHttpClient.Builder()

    fun init(baseUrl:String){
        this.baseUrl = baseUrl
    }

    fun getBaseUrl():String{
        return baseUrl
    }

    fun setTimeoutDuration(timeoutDuration:Long){
        this.timeoutDuration = timeoutDuration
    }

    fun get(baseUrl: String=this.baseUrl):Retrofit{
        var retrofit = retrofitMap[baseUrl]
        if (retrofit == null){
            retrofit = createRetrofit(baseUrl)
            retrofitMap[baseUrl] = retrofit
        }
        return retrofit
    }

    private fun createRetrofit(baseUrl: String):Retrofit{
        val myClient= okHttpBuilder
            .connectTimeout(timeoutDuration, TimeUnit.SECONDS )
            .addInterceptor(MyInterceptor())
            .build()
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(myClient)
            .addCallAdapterFactory(LiveDataCallAdapterFactory())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    // 测试用拦截器
    private class MyInterceptor:Interceptor{
        override fun intercept(chain: Interceptor.Chain): Response {
            val request = chain.request()
            val timeStart = System.nanoTime()
            Log.i(TAG, "intercept: sending request ${request.url()} on ${chain.connection()},header=${request.headers()}")
            val response = chain.proceed(request)
            val timeEnd = System.nanoTime()
            Log.i(TAG, "intercept: received response for ${response.request().url()} ${(timeEnd-timeStart)/0x1e6d} header=${response.headers()}")
            return response
        }
    }
}

测试用 Service

interface LoginService{
    @POST("login/")
    fun getLoginResponse(@Query("username")userName:String,
                         @Query("password")userPwd:String)
    :LiveData<LoginResponse>
}

这里为了便于测试,直接使用了网站的 url api,所以Post 方法的参数直接在 url 中进行了传递。如果要放到body中可以这样写

// 以url的格式进行编码,参数需要使用 @Field() 标注
interface PostInBodyWithUrlFormat{ 
    @FormUrlEncoded 
    @Post("targetUrl/")
    // post 的 body 会被写为 param1=xxx&param2=xxx
    fun postTest(@Field("param1") String param1, @Field("param2") String param2):Call<Any?>
}
// 以JSON的格式进行编码
interface PostInBodyWithJsonFormat{
    @Post("targetUrl/")
    fun postTest(@Body JSONObject params)
}
// 以自定义的Body进行编码
interface PostInBodyWithRequestBody{
    @Post("targetUrl/")
    fun postTest(@Body JSONObject params)
}

fun getRequestBody():RequestBody{
    // 要查询所有注册的 MediaType 请访问 https://www.iana.org/assignments/media-types/media-types.xhtml
    // 要查询常见的MIME Type 请访问 https://zhuanlan.zhihu.com/p/166136721
    return RequestBody.create(MediaType.parse("text/plain; charset=utf-8","I can do anything here"))
}

api 的返回 Json

{"data":{"admin":false,"chapterTops":[],"coinCount":0,"collectIds":[],"email":"","icon":"","id":104201,"nickname":"2642981383","password":"","publicName":"2642981383","token":"","type":0,"username":"2642981383"},"errorCode":0,"errorMsg":""}

Json 对应的 JavaBean 类

data class LoginResponse(val data:Data, val errorCode:Int, val errorMsg:String){
    data class Data(val admin:Boolean, val chapterTops:List<String>, val coinCount:Int,
                    val collectIds:List<Int>, val email:String, val icon:String,
                    val id:Int, val nickname:String, val password:String,
                    val publicName:String, val token:String, val type:Int,
                    val userName:String
    )
}

LiveData测试

private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG, "onCreate: ")
    }

    override fun onStart() {
        super.onStart()
        RetrofitManager.get().create(LoginService::class.java)
            .getLoginLiveData("userName","userPwd") //填入wanandroid用户名和密码
            .observe(this, { loginResponse->
                Log.d(TAG, "onPause: loginResponse=${loginResponse}")
            })
    }
}
Kotlin + 协程 + Retrofit + MVVM 是一种非常优雅的方式来实现网络请求。下面是一个简单的示例: 首先,我们需要添加相关依赖库: ```gradle // Kotlin协程 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' // Retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // ViewModel和LiveData implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0' ``` 接下来,我们创建一个数据类来表示网络请求返回的数据: ```kotlin data class Response<T>( val code: Int, val msg: String, val data: T? ) ``` 然后,我们创建一个接口来描述我们的API: ```kotlin interface ApiService { @GET("api/get_data") suspend fun getData(): Response<List<String>> } ``` 注意,我们在这里使用了 `suspend` 关键字来表示这个方法是一个挂起函数。 接下来,我们创建一个 `ApiService` 的实例: ```kotlin val retrofit = Retrofit.Builder() .baseUrl("https://example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() val api = retrofit.create(ApiService::class.java) ``` 现在,我们可以在我们的 ViewModel 中使用这个 `api` 对象来进行网络请求了: ```kotlin class MyViewModel : ViewModel() { private val _data = MutableLiveData<List<String>>() val data: LiveData<List<String>> = _data fun loadData() { viewModelScope.launch { val response = api.getData() if (response.code == 200) { _data.value = response.data } } } } ``` 在这里,我们使用了 `viewModelScope` 来启动一个协程来进行网络请求。如果请求成功,我们就将数据传递给 `_data` 变量,它是一个 LiveData 对象。 最后,我们可以在我们的 Activity 或 Fragment 中观察 `data` 变量来获取网络请求返回的数据: ```kotlin class MyActivity : AppCompatActivity() { private val viewModel by viewModels<MyViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_my) viewModel.data.observe(this, Observer { data -> // 在这里更新UI }) viewModel.loadData() } } ``` 这样,我们就使用 Kotlin + 协程 + Retrofit + MVVM 实现了一个优雅的网络请求
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值