ktBaseLibrary是基于 MVP + Retrofit + 协程 的android快速开发框架。
之前一直使用rxjava + retrofit。在使用了kotlin之后,接触了协程,现在retrofit已经支持了协程,在使用retrofit+协程之后,果断的抛弃了rxjava。
之所以抛弃了rxjava,主要有以下两个方面:
- 可以以同步的方式写出异步的代码。
- 减少一个rxjava的依赖,可以减小apk的体积
下面是以同步方式写出异步代码的实例:
mainScope.launch {
//网络请求
val singlePoetry = ApiServices.instance.singlePoetry().response()
//singlePoetry是网络请求的结果
if (singlePoetry != null) {
mView?.showResult(singlePoetry)
}
mView?.hideLoading()
}
以上代码简洁,逻辑清晰。
框架主体使用 kotlin + mvp + retrofit + 协程
的模式。另外引用了一些好用的第三方库以及自己封装的一些基础库。具体如下图:
在主项目app的build.gradle中依赖
dependencies {
...
implementation 'com.tbright:ktbaselibrary:1.1.3'
}
在project的build.gradle中配置
allprojects {
repositories {
maven { url 'https://dl.bintray.com/tongsiwei49/ktbaselibrary/' }
}
}
或者 下载到本地导入Module.
需继承 HttpConfigProxy
,这里主要是统一处理错误的请求结果。下面是demo里面实现的一个,具体的大家可以根据自己的项目作修改,详细请看:
HttpConfig
class HttpConfig : HttpConfigProxy() {
private var mRetrofit: Retrofit? = null
private var mRetrofitBuilder: Retrofit.Builder? = null
private var mOkHttpClientBuilder: OkHttpClient.Builder? = null
//
override var baseUrl: String = mBaseUrl
override var baseUrls: Map<String, String>
set(value) {}
get() {
var urls = linkedMapOf<String, String>()
if (urls.isNotEmpty()) return urls
if (GlobalConfig.isDebug) {//可以在这里动态切换服务
urls[BASE_URL] = mBaseUrl
urls[GANK_URL] = mGankUrl
} else {
urls[BASE_URL] = mBaseUrl
urls[GANK_URL] = mGankUrl
}
return urls
}
override suspend fun <T> parseResponseData(responseData: Deferred<BaseResponse<T>>): T? {
try {
var response = responseData.await()
if (response.isResponseSuccess()) {
return response.getResponseData()
} else {
MessageEvent( EVENTCODE_RESPONSE_FAIL,response.getResponseMessage()).send()
return null
}
} catch (e: Throwable) {
var errMsg = "网络异常"
when (e) {
is UnknownHostException -> errMsg = "连接失败"
is ConnectException -> errMsg = "连接失败"
is SocketTimeoutException -> errMsg = "连接超时"
is InterruptedIOException -> errMsg = "连接中断"
is SSLHandshakeException -> errMsg = "证书验证失败"
is JSONException -> errMsg = "数据解析错误"
is JsonSyntaxException -> errMsg = "数据解析错误"
is NoNetworkException -> errMsg = "无可用网络"
is HttpException -> {
when (e.code()) {
UNAUTHORIZED, FORBIDDEN -> {//这两个一般会要求重新登录
MessageEvent(EVENTCODE_RELOGIN,errMsg).send()
return null
}
}
}
else -> errMsg = e.message.toString()
}
MessageEvent(EVENTCODE_RESPONSE_FAIL, errMsg).send()
}
return null
}
override suspend fun <T> parseResponseWrapperData(responseData: Deferred<BaseResponse<T>>,vararg needDisposeError:Any): BaseResponse<T>? {
try {
var response = responseData.await()
if (response.isResponseSuccess()) {
return response
} else {
if(needDisposeError.contains(response.getResponseStatus())){//如果包含,不统一处理,在相应的页面特殊处理
return response
}else{
MessageEvent( EVENTCODE_RESPONSE_FAIL,response.getResponseMessage()).send()
return null
}
}
} catch (e: Throwable) {
var errMsg = "网络异常"
when (e) {
is UnknownHostException -> errMsg = "连接失败"
is ConnectException -> errMsg = "连接失败"
is SocketTimeoutException -> errMsg = "连接超时"
is InterruptedIOException -> errMsg = "连接中断"
is SSLHandshakeException -> errMsg = "证书验证失败"
is JSONException -> errMsg = "数据解析错误"
is JsonSyntaxException -> errMsg = "数据解析错误"
is NoNetworkException -> errMsg = "无可用网络"
is HttpException -> {
when (e.code()) {
UNAUTHORIZED, FORBIDDEN -> {//这两个一般会要求重新登录
MessageEvent(EVENTCODE_RELOGIN,errMsg).send()
return null
}
}
}
else -> errMsg = e.message.toString()
}
MessageEvent(EVENTCODE_RESPONSE_FAIL, errMsg).send()
}
return null
}
override fun initRetrofit() {
initClient()
mRetrofitBuilder = Retrofit.Builder()
mRetrofit = mRetrofitBuilder?.run {
baseUrl(GlobalConfig.httpConfigProxy?.baseUrl ?: GlobalConfig.httpConfigProxy?.baseUrls!!.values.first())//如果项目就一个域名,可以直接使用baseUrl,baseUrls可以不用管
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.client(mOkHttpClientBuilder!!.build())
.build()
build()
}
}
private var mRetrofitServices = hashMapOf<String, Any>()
@Suppress("UNCHECKED_CAST")
override fun <T> create(clazz: Class<T>): T {
var key = clazz.canonicalName
var mRetrofitService = mRetrofitServices[key]
if (mRetrofitService == null) {
mRetrofitService = mRetrofit!!.create(clazz)
mRetrofitServices[key!!] = mRetrofitService!!
}
return mRetrofitService as T
}
private fun initClient() {
mOkHttpClientBuilder = OkHttpClient.Builder()
mOkHttpClientBuilder?.run {
if (GlobalConfig.isDebug) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
//设置 Debug Log 模式
addInterceptor(loggingInterceptor)
}
connectTimeout(TIME_OUT, TimeUnit.SECONDS)
readTimeout(TIME_OUT, TimeUnit.SECONDS)
writeTimeout(TIME_OUT, TimeUnit.SECONDS)
//错误重连
retryOnConnectionFailure(true)
addInterceptor(CacheInterceptor())
addInterceptor(HeaderInterceptor())
addInterceptor(MultiUrlInterceptor())
}
}
}
需继承 ShowUIProxy
。它的作用主要是统一显示请求网络是需要显示的loading框。下面是demo里面实现的一个,具体的大家可以根据自己的项目作修改,详细请看:
ShowUIConfig
class ShowUIConfig : ShowUIProxy {
override fun <T> parseResponseFailMessage(messageEvent: MessageEvent<T>) {
when (messageEvent.code) {
EVENTCODE_RESPONSE_FAIL -> {
hideLoading()
if (messageEvent.data != null) {
var errorMessage = messageEvent.data as String
showError(errorMessage)
} else {
showError("网络错误")
}
}
EVENTCODE_RELOGIN -> {//重新登录
hideLoading()
getTopActivity().reLogin()
}
}
}
override fun showLoading() {
getTopActivity().showLoadingDialog()
}
override fun hideLoading() {
getTopActivity().hideLoadingDialog()
}
override fun showError(errorMessage: String) {
errorMessage.showToast()
}
}
这个是必须的
GlobalConfig.init(this,httpConfigProxy = HttpConfig(),showUIProxy = ShowUIConfig())
继承BaseMvpActivity
class MainActivity : BaseMvpActivity<MainPresenter>(),MainContract.MainView {
override fun getLayoutId(): Int {
return R.layout.activity_main
}
override fun initView(savedInstanceState: Bundle?) {
}
override fun initData() {
}
}
当然如果页面很简单,没有什么逻辑要处理,也可以继承 BaseActivity
。
class MainActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_main
}
override fun initView(savedInstanceState: Bundle?) {
}
override fun initData() {
}
}
Fragment
的使用方式和 Activity
差不多,继承 BaseMvpFragment
。
class MainFragment : BaseMvpFragment<MainFragmentPresenter>(),MainFragmentContract.MainView {
override fun getLayoutId(): Int {
return R.layout.activity_main
}
override fun initView(savedInstanceState: Bundle?) {
}
override fun initData() {
}
}
当然也可以直接继承 BaseFragment
,与直接继承 Activity
一样。
Presenter
需继承 BasePresenter
,View
需继承 BaseView
。下面是demo的契约类,可以参考:
interface MainContract {
abstract class MainPresenter : BasePresenter<BaseModel, MainView>(){
abstract fun singlePoetry()
...
}
interface MainView : BaseView{
fun showResult(result : String)
}
}
下面是MainPresenter的实例代码:
class MainPresenter : MainContract.MainPresenter() {
override fun singlePoetry() {
mView?.showLoading()
mainScope.launch {
//网络请求
val singlePoetry = ApiServices.instance.singlePoetry().response()
//singlePoetry是网络请求的结果
if (singlePoetry != null) {
mView?.showResult(singlePoetry)
}
mView?.hideLoading()
}
}
}
可以看见,网络请求的逻辑非常清晰。
具体的实例,可以参考demo。
后续会进一步完善,欢迎大家多都提意见
QQ群(1105357684)