基于RxJava和Kotlin封装类处理网络加载数据

在Android开发里,常常需要处理网络加载数据,这里基于RxJava,和Kotlin的Sealed class(封装类)的特性实现的一个示例。

Resource.kt

首先使用Kotlin的sealed class实现Resource.kt,它用来包装数据和状态。

sealed class Resource<out T> {
    class Loading<out T> : Resource<T>()
    data class Success<out T>(val data: T?) : Resource<T>()
    data class Failure<out T>(val throwable: Throwable) : Resource<T>()
}

其中类型参数T使用了修饰符out来标记,这是为了确保它只能由Resource的成员(Loading,Success和Failure)生成返回,而不能被消费使用。

Content.isNetworkStatusAvailable

使用Kotlin的扩展特性给Content添加isNetworkStatusAvailable扩展,它用于检测网络状态是否可用。

fun Context.isNetworkStatusAvailable(): Boolean {
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
    connectivityManager?.let {
        it.activeNetworkInfo?.let {
            if (it.isConnected) return true
        }
    }
    return false
}

NetworkBoundResource

NetworkBoundResource网络绑定资源类,它主要用来监察数据库。根据是否有网络来处理数据:

  1. 网络不可用,直接从数据库获取数据分发出去。
  2. 网络可用时,为了避免用户长时间看到等待加载数据,它会先从数据库获取数据分发,然后等待网络加载完数据后再更新数据库,最后再从数据库获取数据分发出去。
abstract class NetworkBoundResource<ResultType, RequestType>(context: Context) {

    private val result: Flowable<Resource<ResultType>>

    init {
        // Lazy disk observable.
        val diskObservable = Flowable.defer {
            loadFromDb()
                    // Read from disk on Computation Scheduler
                    .subscribeOn(Schedulers.computation())
        }

        // Lazy network observable.
        val networkObservable = Flowable.defer {
            createCall()
                    // Request API on IO Scheduler
                    .subscribeOn(Schedulers.io())
                    // Read/Write to disk on Computation Scheduler
                    .observeOn(Schedulers.computation())
                    .doOnNext { request: Response<RequestType> ->
                        if (request.isSuccessful) {
                                saveCallResult(processResponse(request))
                        }
                    }
                    .onErrorReturn { throwable: Throwable ->
                        when (throwable) {
                            is HttpException -> {
                                throw Exceptions.propagate(NetworkExceptions.getNoServerConnectivityError(context))
                            }
                            is IOException -> {
                                throw Exceptions.propagate(NetworkExceptions.getNoNetworkConnectivityError(context))
                            }
                            else -> {
                                throw Exceptions.propagate(NetworkExceptions.getUnexpectedError(context))
                            }
                        }
                    }
                    .flatMap { loadFromDb() }
        }

        result = when {
            context.isNetworkStatusAvailable() -> networkObservable
                    .map<Resource<ResultType>> { Resource.Success(it) }
                    .onErrorReturn { Resource.Failure(it) }
                    // Read results in Android Main Thread (UI)
                    .observeOn(AndroidSchedulers.mainThread())
                    .startWith(Resource.Loading())
            else -> Flowable.concat(diskObservable, networkObservable)
                    .map<Resource<ResultType>> { Resource.Success(it) }
                    .onErrorReturn { Resource.Failure(it) }
                    // Read results in Android Main Thread (UI)
                    .observeOn(AndroidSchedulers.mainThread())
                    .startWith(Resource.Loading())
        }
    }

    fun asFlowable(): Flowable<Resource<ResultType>> {
        return result
    }

    private fun processResponse(response: Response<RequestType>): RequestType {
        return response.body()!!
    }

    protected abstract fun saveCallResult(request: RequestType)

    protected abstract fun loadFromDb(): Flowable<ResultType>

    protected abstract fun createCall(): Flowable<Response<RequestType>>
}

Repository

一般会使用Repository来分离获取数据,并映射数据到业务模型里,示例代码如下:

fun loadContents(): Flowable<Resource<List<Content>>> {
        return object : NetworkBoundResource<List<Content>, NetworkResponse>(context) {
            override fun saveCallResult(request: NetworkResponse) {
                contentDao.insertContents(request.data)
            }

            override fun loadFromDb(): Flowable<List<Content>> {
                return contentDao.getContents()
            }

            override fun createCall(): Flowable<Response<NetworkResponse>> {
                return contentService.getContents()
            }

        }.asFlowable()
    }

Activity

在Activity里请求和处理加载数据如下:

private fun loadContents() {
        disposables.add(contentsViewModel.loadContents()
                .subscribe({ t: Resource<List<Content>> ->
                    when (t) {
                        is Resource.Loading -> {
                            // show loading state
                        }
                        is Resource.Success -> {
                            // show data
                        }
                        is Resource.Failure -> {
                            // show error state
                        }
                        else -> throw IllegalStateException("State not known or implemented.")
                    }
                }, { t: Throwable ->
                    throw OnErrorNotImplementedException(t) // Explicitly throw this exception to debug.
                }, { Timber.e("Completed.") }))
    }

参考:https://android.jlelse.eu/networkboundresource-with-rxjava-and-kotlin-sealed-classes-1574bc516f82

版权声明:著作权归作者所有。

相关推荐

PHP类自动加载spl_autoload_register()

在PHP有两种方式实现类的自动加载:__autoload()和spl_autoload_register()。但由于__autoload()是我们自己定义的函数,它只允许被定义一次,这样很容易导致冲突,显得很不灵活,php 7已经把__autoload()废弃。spl_autoload_register()是spl标准库提供的函数,它允许我们多次调用,根据需要加载多个不同的autoloa

Kotlin使用kotlin-kapt插件支持Android的注解处理

在Kotlin可以使用kapt插件来支持Android的注解处理。在Gradle配置kotlin-kapt插件如下:在app的build.gradle添加插件apply plugin: 'kotlin-kapt' 使用kapt添加注解依赖java使用annotationProcessor 添加的依赖改为使用kapt。例如添加dagger依赖dependencies {

Kotlin:类的定义

基本定义Kotlin使用关键词class定义类,如:class User { } 声明类主要包括三部分:类名:必选,类的名称,一般以大写字母开头。类头:可选,类头包括type parameter(如泛型),主构造(primary constructor)等。类体:可选,在Kotlin,类体是可选的,它有大括号{}括起来。类头和类体是可选的,一个最简单的类可

Kotlin: package和import

packageKotlin的代码结构是通过源文件和package组织。在源文件的开头声明package,如package foo.bar fun baz() {} class Goo {} // ... 在声明的package源文件里的所有内容均属于此package。如示例里的baz函数和类Goo全名分别为foo.bar.baz和f

Kotlin空值(Nullable Value)的处理以及转换

 使用Java稍不留神很容易导致NullPointException。Kotlin的类型系统使用可空类型(Nullable Type)旨在消除null引用导致的代码出错。Kotlin要求我们必须检测可空类型的值,只有非null的值才允许访问。有几种方式来处理可空类型的值,以避免null引用的出现。 条件检查var nullableStr?: Str