基于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网络绑定资源类,它主要用来监察数据库。根据是否有网络来处理数据:
- 网络不可用,直接从数据库获取数据分发出去。
- 网络可用时,为了避免用户长时间看到等待加载数据,它会先从数据库获取数据分发,然后等待网络加载完数据后再更新数据库,最后再从数据库获取数据分发出去。
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.") }))
    }
 
             
             
             
             
            