本篇博客主要浅显的讲讲如何使用kotlin+RxJava2++Retrofit2+MVP做网络框架封装
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'
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
}
到这里就算封装完成了,接下来说一下具体使用
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
}
}