用kotlin语言实现RxJava2+Retrofit2+MVP做网络框架封装

1.简介

  • 不知道什么时候,kotlin被google指定为官方开发语言,所以学习一下还是有必要的,这里主要说明使用kotlin做网络框架封装。

2.特点

3.说明

本篇博客主要浅显的讲讲如何使用kotlin+RxJava2++Retrofit2+MVP做网络框架封装

4.代码分析

  • 4.1.添加依赖

app下的build.grade文件的目录dependencies下添加

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
  • 4.2.HttpBase封装代码

NetService.kt

  • 和java代码类似,主要就是定义接口的url,请求类型,传参
interface NetService {
    /**
     * 请求天气接口
     *
     * @param cityName 城市名称
     */
    @GET("weatherApi")
    fun cityWeather(@Query("city") cityName: String): Flowable>
}

BaseHttp.kt

  • 主要做了网络请求的相关配置:设定请求,响应,连接时间;添加日志拦截器;添加请求头head;将retrofit2和okhttp结合起来。
object BaseHttp {
    private val TIME_OUT: Long = 10000

    val baseHttp: NetService
        get() = Builder()
                .setBaseUrl("https://www.apiopen.top/")
                .setLogInterceptor(true)
                .builder()

    class Builder {
        private var mBaseUrl: String? = null
        private var mLogInterceptor: Boolean = false

        fun setBaseUrl(baseUrl: String): Builder {
            this.mBaseUrl = baseUrl
            return this
        }

        fun setLogInterceptor(logInterceptor: Boolean): Builder {
            this.mLogInterceptor = logInterceptor
            return this
        }

        fun builder(): NetService {
            val okHttpClient = OkHttpClient.Builder()
            okHttpClient.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
            okHttpClient.readTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
            okHttpClient.writeTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
            okHttpClient.addInterceptor { chain ->
                val request = chain.request()
                val builder = request.newBuilder()
                        .addHeader("client", "android")
                        .header("Content-Type", "application/json")
                        .method(request.method(), request.body())
                        .build()
                chain.proceed(builder)
            }
            if (mLogInterceptor) {
                okHttpClient.addInterceptor(ResponseLog())//日志插值器
            }
            return Retrofit.Builder()
                    .baseUrl(mBaseUrl!!)
                    .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(okHttpClient.build())
                    .build()
                    .create(NetService::class.java)
        }
    }
}

HttpCodeException.kt

  • http请求error状态处理的一种,结合下面的AppException.kt就知道了,
class HttpCodeException(code: Int, s: String) : Exception(s) {

    var code: Int = 0
        internal set

    init {
        this.code = code
    }
}

BaseBean.kt

  • 一般来说接口返回的JavaBean类最外层都是code,msg,data做封装处理,当然也不一定要必须这么做,下面会有说明
class BaseBean {

    var code: Int = 0
    var msg: String? = null
    var data: T? = null
}

AppException.kt

  • 统一做封装
class AppException : Function, Flowable> {
    override fun apply(baseBean: BaseBean): Flowable {
        return if (baseBean.code != 200) {
            Flowable.error(baseBean.msg?.let { HttpCodeException(baseBean.code, it) })
            //说明一下上面的这种写法在java代码中是这样
            // Flowable.error(new HttpCodeException(baseBean.getCode(), baseBean.getMsg()));
        } else Flowable.just(baseBean.data!!)
    }
}

BaseBean.kt

class BaseBean {
    var code: Int = 0
    var msg: String? = null
    var data: T? = null
}

到这里就算封装完成了,接下来说一下具体使用

  • 4.3.示例代码

IWeatherModel.kt

interface IWeatherModel {
    fun cityWeather(cityName: String, listener: CityWeatherListener)

    interface CityWeatherListener {

        fun responseWeatherSuccess(bean: WeatherBean)

        fun responseWeatherFail(msg: String)
    }
}

WeatherModel.kt

class WeatherModel : IWeatherModel {
    override fun cityWeather(cityName: String, listener: IWeatherModel.CityWeatherListener) {
        BaseHttp.baseHttp.cityWeather(cityName)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()) // 请求都完成后,切换到UI线程,将结果进行渲染
                .flatMap(AppException())
                .subscribe(object : ResourceSubscriber() {
                    override fun onComplete() {
                    }

                    override fun onNext(t: WeatherBean) {
                        listener.responseWeatherSuccess(t)
                    }

                    override fun onError(e: Throwable) {
                    	//类似listener.responseWeatherFail(e.message);
                        e.message?.let { listener.responseWeatherFail(it) }
                    }
                })
    }
}

//这里再提供一个code异常类
private fun errorMsg(e: Throwable): String {
        if (e is HttpCodeException) {
            val httpException = e as HttpCodeException
            Log.e("error", "------httpException")
            return if (e.message == null) "网络错误..." else e.message!!
        } else if (e is HttpException) {//对网络异常 打出相应的log
            val errorMsg = e.message
            return errorMsg ?: "网络错误..."
        } else if (e is JsonParseException || e is JSONException || e is ParseException) {//解析异常
            return "解析异常..."
        } else if (e is UnknownHostException) {
            return "域名解析错误..."
        } else if (e is SocketTimeoutException) {
            return "网络链接超时..."
        } else if (e is MalformedJsonException) {
            return if (e.message == null) "网络错误..." else e.message!!
        }
        return "网络错误..."
    }

说明:flatMap(AppException())主要是接口返回外层符合BaseBean的时候才这样调用,如果不符合,不需要指定flatMap(),需要在onNext(t:T)里面做单独判断code是否等于200;因为接口请求成功会走onNext(t:T)里面去,但是一般我们默认code==200才算真正成功,上面的flatMap(AppException())方法是将虽然请求成功但code不等于200的给抛到 onError(e: Throwable)里面了。如果不指定,需要在onNext(t:T)里面判断code是否等于200。

WeatherContract.kt

interface WeatherContract {
    interface View {
        fun getCityWeatherSuccess(bean: WeatherBean)

        fun getCityWeatherFail(error: String)
    }

    interface Presenter {
        fun cityName(cityName: String)
    }
}

WeatherPresenter.kt

class WeatherPresenter internal constructor( private val mView: WeatherContract.View) : WeatherContract.Presenter {
    private val mModel: WeatherModel = WeatherModel()

    override fun cityName(cityName: String) {
        mModel.cityWeather(cityName, object : IWeatherModel.CityWeatherListener {
            override fun responseWeatherSuccess(bean: WeatherBean) {
                mView.getCityWeatherSuccess(bean)
            }

            override fun responseWeatherFail(msg: String) {
                mView.getCityWeatherFail(msg)
            }
        })
    }
}

WeatherActivity.kt

class WeatherActivity : AppCompatActivity(), WeatherContract.View {

    private var presenter: WeatherPresenter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        presenter = WeatherPresenter( this)
        etCity.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable) {

            }

            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            }
        })

        btnStart.setOnClickListener {
            val cityName = etCity.text.toString().trim();
            if (TextUtils.isEmpty(cityName)) {
                Toast.makeText(this, "请输入城市名称", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            presenter!!.cityName(cityName)

        }
    }

    @SuppressLint("SetTextI18n")
    override fun getCityWeatherSuccess(bean: WeatherBean) {
        Toast.makeText(this, "网络请求成功", Toast.LENGTH_SHORT).show()
        if (bean.forecast != null) {
            val forecast = bean.forecast
            tvContent.text = bean.forecast!![0].date + "\n" + forecast?.get(0)!!.fengxiang + 
            forecast[0].fengli + "\n" + forecast[0].type + "\n" + bean.ganmao
        }
    }

    override fun getCityWeatherFail(error: String) {
        Toast.makeText(this, "网络请求失败", Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        presenter = null
    }
}

5.参考

  • 如果觉得不清楚,请参照项目源码:Kotlin_baseHttp
  • 如果不明白MVP设计模式,请参考我的这一篇:解析MVC和MVP设计模式的使用及优缺点

6.总结

你可能感兴趣的:(android)