Kotlin-Retrofit2和Rxjava2的网络封装,展示Github的用户信息

目录

开始

1.先添加依赖 

2.封装请求类

3.RESTful API请求响应的处理

4.线程与生命周期

5.使用

效果如下:


开始

1.先添加依赖 

//Retrofit相关
    implementation(['com.squareup.okhttp3:logging-interceptor:3.9.0',//用于查看http请求时的log
                    'com.squareup.retrofit2:retrofit:2.3.0',
                    'com.squareup.retrofit2:adapter-rxjava2:2.3.0',
                    'com.squareup.retrofit2:converter-gson:2.3.0'])

//RxJava相关
    implementation(['io.reactivex.rxjava2:rxandroid:2.0.1',
                    'io.reactivex.rxjava2:rxjava:2.1.3'])
     

//RxLifecycle相关
    implementation(['com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.0',
                    'com.trello.rxlifecycle2:rxlifecycle-components:2.2.0'])

2.封装请求类

 Retrofit简单封装秉承RxJava的链式调用风格,也为了方便每一个API的调用操作,创建了一个单例类ApiClient,具体如下:

class ApiClient private constructor() {
    lateinit var service: GitHubService

    private object Holder {
        val INSTANCE = ApiClient()
    }
    companion object {
        val instance by lazy { Holder.INSTANCE }
    }
    fun init() {//在Application的onCreate中调用一次即可
        val okHttpClient = OkHttpClient().newBuilder()
                //输入http连接时的log,也可添加更多的Interceptor
                .addInterceptor(HttpLoggingInterceptor().setLevel(
                        if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
                        else HttpLoggingInterceptor.Level.NONE
                )).build()
        val retrofit = Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .build()
        service = retrofit.create(GitHubService::class.java)
    }
}

  在这里使用GitHubAPI作为测试,GitHubService如下:

interface GitHubService {
    //请添加相应的`API`调用方法
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Observable<List<Repo>>//每个方法的返回值即一个Observable
}

上面的Repo即一个简单的Kotlin数据类,由于字较多,就不贴出来了,具体可去Demo查找。

3.RESTful API请求响应的处理

API的响应返回形式有很多种,此处介绍标准RESTful APIGitHub提供的API即标准RESTful API

先在values写一下错误描述:

 <resources>
    <string name="service_error">服务器出错</string>
    <string name="not_found">请求的资源不存在</string>
    <string name="timeout">请求超时</string>
    <string name="unexpected_error">未知错误</string>
    <string name="network_wrong">网络有问题</string>
</resources>

RESTful API的请求响应主要处理状态码与数据体,具体封装如下:

abstract class ApiResponse<T>(private val context: Context) : Observer<T> {
    abstract fun success(data: T)
    abstract fun failure(statusCode: Int, apiErrorModel: ApiErrorModel)


    override fun onSubscribe(d: Disposable) {
        LoadingDialog.show(context)
    }

    override fun onNext(t: T) {
        success(t)
    }

    override fun onComplete() {
        LoadingDialog.cancel()
    }

    override fun onError(e: Throwable) {
        LoadingDialog.cancel()
        if (e is HttpException) {//连接服务器成功但服务器返回错误状态码
            val apiErrorModel: ApiErrorModel = when (e.code()) {
                ApiErrorType.INTERNAL_SERVER_ERROR.code ->
                    ApiErrorType.INTERNAL_SERVER_ERROR.getApiErrorModel(context)
                ApiErrorType.BAD_GATEWAY.code ->
                    ApiErrorType.BAD_GATEWAY.getApiErrorModel(context)
                ApiErrorType.NOT_FOUND.code ->
                    ApiErrorType.NOT_FOUND.getApiErrorModel(context)
                else -> otherError(e)
            }
            failure(e.code(), apiErrorModel)
            return
        }
        val apiErrorType: ApiErrorType = when (e) {//发送网络问题或其它未知问题,请根据实际情况进行修改
            is UnknownHostException -> ApiErrorType.NETWORK_NOT_CONNECT
            is ConnectException -> ApiErrorType.NETWORK_NOT_CONNECT
            is SocketTimeoutException -> ApiErrorType.CONNECTION_TIMEOUT
            else -> ApiErrorType.UNEXPECTED_ERROR
        }
        failure(apiErrorType.code, apiErrorType.getApiErrorModel(context))
    }

    private fun otherError(e: HttpException) =
            Gson().fromJson(e.response().errorBody()?.charStream(), ApiErrorModel::class.java)
}

说明:
1.每个响应继承Observer,其中的泛型以适配返回的不同的数据体;
2.定义两个抽象方法successfailure,在使用的时候只需关注成功和失败这两种情况;
3.在onSubscribe即开始请求的时候显示Loading,在请求完成或出错时隐藏;
4.在onNextObserver成功接收数据后直接调用success,在调用处可直接使用返回的数据;
5.在onError即请求出错时处理,此处包含两种情况:连接服务器成功但服务器返回错误状态码、网络或其它问题。

这个是读条时的LoadingDialog代码:

dialog_loading.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ProgressBar
        style="@android:style/Widget.ProgressBar.Inverse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

</LinearLayout>

styles:

    <style name="LoadingDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>

LoadingDialog:

object LoadingDialog {
    private var dialog: Dialog? = null

    fun show(context: Context) {
        cancel()
        dialog = Dialog(context, R.style.LoadingDialog)
        dialog?.setContentView(R.layout.dialog_loading)
        dialog?.setCancelable(false)
        dialog?.setCanceledOnTouchOutside(false)
        dialog?.show()
    }
    fun cancel() {
        dialog?.dismiss()
    }
}

在错误处理中,定义了一个枚举类ApiErrorType,用于列举出服务器定义的错误状态码情况:

enum class ApiErrorType(val code: Int, @param:StringRes private val messageId: Int) {
    //根据实际情况进行增删
    INTERNAL_SERVER_ERROR(500, R.string.service_error),
    BAD_GATEWAY(502, R.string.service_error),
    NOT_FOUND(404, R.string.not_found),
    CONNECTION_TIMEOUT(408, R.string.timeout),
    NETWORK_NOT_CONNECT(499, R.string.network_wrong),
    UNEXPECTED_ERROR(700, R.string.unexpected_error);

    private val DEFAULT_CODE = 1
    fun getApiErrorModel(context: Context): ApiErrorModel {
        return ApiErrorModel(DEFAULT_CODE, context.getString(messageId))
    }
}

还定义了一个错误消息的的实体类ApiErrorModel(在Kotlin中即为一个数据类),用于包含错误信息提示用户或服务器返回的错误信息以提示开发人员:

data class ApiErrorModel(var status: Int, var message: String)

4.线程与生命周期

RxJava的一大特色即方便的线程切换操作,在请求API中需要进行线程的切换,通常是以下形式(伪代码):

observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

但每个请求都写一段这个,显得特别麻烦,所以进行以下简单封装:

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

Android中,当一个Activity在调APIonDestroy了,需要取消请求,所以此处引入了RxLifecycle进行管理:
Activity继承RxAppCompatActivity后,在observable的调用链中加入.bindUntilEvent(this, ActivityEvent.DESTROY)即可

5.使用

以上准备工作完成后,即可开始使用:
首先在Application中初始化ApiClient

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        ApiClient.instance.init()
    }
}

在需要的地方使用ApiClient,如本文Demo,点击按钮时,请求数据,成功后用TextView显示出来:

class MainActivity : RxAppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        submit.setOnClickListener { fetchRepo() } //按钮点击事件
    }
    private fun fetchRepo() {
        //链式调用
        ApiClient.instance.service.listRepos(inputUser.text.toString()) //GitHubService中的方法
                .compose(NetworkScheduler.compose())                         //线程切换处理
                .bindUntilEvent(this, ActivityEvent.DESTROY)        //生命周期管理
                .subscribe(object : ApiResponse<List<Repo>>(this) {              //对象表达式约等于Java中的匿名内部类
                    override fun success(data: List<Repo>) {                     //请求成功,此处显示一些返回的数据
                         userName.text = data[0].owner.login   //用户名
                        repoName.text = data[0].name            //仓库名
                        description.text = data[0].description  //描述
                        url.text = data[0].html_url         //url
                    }
                    override fun failure(statusCode: Int, apiErrorModel: ApiErrorModel) {
                        Toast.makeText(this@MainActivity, apiErrorModel.message, Toast.LENGTH_SHORT).show()
                    }
                })
    }
}

效果如下

Demo地址:https://github.com/qingyinz/RxjavaRetrofitDemo3
参考链接:http://www.jianshu.com/p/c66d50cd14ee

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值