目录
开始
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)
}
}
在这里使用GitHub
的API
作为测试,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 API
。GitHub
提供的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.定义两个抽象方法success
和failure
,在使用的时候只需关注成功和失败这两种情况;
3.在onSubscribe
即开始请求的时候显示Loading
,在请求完成或出错时隐藏;
4.在onNext
即Observer
成功接收数据后直接调用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
在调API
时onDestroy
了,需要取消请求,所以此处引入了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