kotlin网络

Kotlin基于Android,结合Retrofit2+Rxja

2019-03-31  本文已影响0人  STE北京老徐
image.png

KotlinFrameWork

本实例封装Kotlin网络核心库以及重要核心逻辑
github链接:https://github.com/KsonCode/KotlinFrameWork

开始

1.app模块:

添加依赖

//retrofit相关
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
//rxjava2
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//rxlifecycle
implementation 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.0'
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.0'

NOTE:可以去RetrofitRxjava2(RxAndroid)okhttpRxLifecycle,查询最新版本号。

2.net库之封装请求类

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

class RetrofitManager private constructor() {

//    lateinit var apiService: ApiService
    lateinit var  retrofit:Retrofit

    /**
     * 单例模式
     */
    companion object {
        val instance: RetrofitManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {

            RetrofitManager()
        }
    }

    fun init(string: String) {
        val okHttpClient =
            OkHttpClient.Builder()
                .addInterceptor(HttpLoggingInterceptor().setLevel(
                    if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
                    else HttpLoggingInterceptor.Level.NONE))
                .connectTimeout(5,TimeUnit.SECONDS)
                .readTimeout(5,TimeUnit.SECONDS)
                .writeTimeout(5,TimeUnit.SECONDS)
                .build()
        retrofit = Retrofit.Builder()
            .baseUrl(string)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(okHttpClient)
            .build()

//        apiService = retrofit.create(ApiService::class.java)

    }

    /**
     * 动态代理模式,创建请求接口类
     * @param tClass
     * @param <T>
     * @return
    </T> */
    fun <T> createService(tClass: Class<T>): T {

        return retrofit.create(tClass)
    }
}

其中接口声明类:GitHubService如下:

/**
 * 接口声明类
 */
interface ApiService{
    /**
     * 登录
     */
    @POST
    @FormUrlEncoded
    fun login(@Url string: String, @Field("phone") mobile:String, @Field("pwd")pwd: String): Observable<UserBean>

}

上面的UserBean即一个简单的Kotlin数据类,可以去这里查看。

3.支持RESTful API请求响应的处理

API的响应返回形式有很多种,此处介绍最常见的两种形式的处理:标准RESTful API任性的后端写的API

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

/**
 * 封装响应数据,统一异常处理
 */
abstract class NetResponseObserver<T>(private val context: Context):Observer<T>{

    /**
     * 事件接收完毕
     */
    override fun onComplete() {
        LoadingDialog.cancel()
    }

    /**
     * 订阅事件的回调
     */
    override fun onSubscribe(d: Disposable) {
        LoadingDialog.show(context)
    }

    /**
     * 接收事件
     */
    override fun onNext(t: T) {
        success(t)
    }

    /**
     * 成功的回调
     */
    abstract fun success(data: T)

    /**
     * 失败的回调
     */
    abstract fun failure(statusCode: Int, apiErrorModel: ApiErrorModel)

    /**
     * 异常处理
     */
    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即请求出错时处理,此处包含两种情况:连接服务器成功但服务器返回错误状态码、网络或其它问题。

在错误处理中,定义了一个枚举类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 NetScheduler{
    fun <T> compose():ObservableTransformer<T,T>{

        return ObservableTransformer {
            observable -> observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
        }
    }
}

使用的时候简单搞定,伪代码如下:

observable.compose(NetScheduler.compose())

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

observable.compose(NetScheduler.compose())
          .bindUntilEvent(this, ActivityEvent.DESTROY)  //加入这句
          .subscribe(...)

5.使用

在以上准备工作完成后,即可开始使用:

首先在Application中初始化ApiClient

class App : Application() {
    override fun onCreate() {
        super.onCreate()
          RetrofitManager.instance.init(Api.BASE_URL)
    }
}

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

class MainActivity : RxAppCompatActivity(), View.OnClickListener {



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()

    }

    private fun initView() {

        login.setOnClickListener(this)

    }

    /**
     * 登录测试
     */
    fun login() {

        //链式调用
        RetrofitManager.instance.createService(ApiService::class.java).login(Api.LOGIN_URL,"18612991023","111111")
            .compose(NetScheduler.compose())
            .bindUntilEvent(this, ActivityEvent.DESTROY)
            .subscribe(object : NetResponseObserver<UserBean>(this){
                override fun success(data: UserBean) {
                    Toast.makeText(this@MainActivity,data.result.phone,Toast.LENGTH_SHORT).show()
                }

                override fun failure(statusCode: Int, apiErrorModel: ApiErrorModel) {

                }

            })

    }

    /**
     * 点击事件
     */
    override fun onClick(v: View?) {
        when (v!!.id) {
            R.id.login -> login()
            R.id.reg -> reg()
        }
    }

    /**
     * 注册测试
     */
    private fun reg() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

6.任性的后端写的API请求响应的处理

这种情况只需要对数据类和响应处理进行修改即可。有些后端开发者们,可能将返回体写成如下形式:

{
    "result": {
        "headPic": "http://mobile.bwstudent.com/images/small/head_pic/2019-02-26/20190226233015.jpeg",
        "nickName": "Jr_09b24",
        "phone": "18612991023",
        "sessionId": "1553950250180251",
        "sex": 1,
        "userId": 251
    },
    "message": "登录成功",
    "status": "0000"
}

所有返回的数据中,最外层都包裹了一层信息,以表示请求成功或失败,中间data才是具体数据,所以定义数据类(实体类)时,需要定义成如下形式:

/**
 * 实体类:通过jsontokotlin插件生成
 */
data class UserBean(
    val message: String,
    val result: Result,
    val status: String
)

data class Result(
    val headPic: String,
    val nickName: String,
    val phone: String,
    val sessionId: String,
    val sex: Int,
    val userId: Int
)

2019年3月31日更新

后续新增上传图片的方法,敬请期待

上一篇下一篇

猜你喜欢

热点阅读