快速打造Kotlin+MVP+Rxjava+Retrofit項目架構(gòu)(引用Google推薦架構(gòu)模式:契約接口)
文件目錄:
主要代碼示例:
- 1溯泣、一個登錄模型(也叫bean、entity)
/**
* 后臺規(guī)定的接口命名的字段是大寫開頭
*/
class LoginModel : Base() {
/**
* Ext : {"Authority":3,"Token":"213bfc688a7e099b1f2a27b64f59c9e1"}
* Message : 登錄成功
* State : true
*/
var ext: ExtBean? = null
class ExtBean {
/**
* Authority : 3
* Token : 213bfc688a7e099b1f2a27b64f59c9e1
*/
var authority: Int = 0
var token: String? = null
}
}
- 2、一個登錄的契約接口
interface View : BaseContract.BaseView {
fun loginSus(loginModel: LoginModel) //登錄成功
fun err(code: Int, message: String) //出錯
}
interface Presenter : BaseContract.BasePresenter<View> {
fun login(telephoneNumber: String, verificationCode: String, token: String) //登錄
}
順便貼出BaseContract契約接口基類
/**
* 對應(yīng)mvp中的contract契約接口
*/
interface BaseContract {
interface BasePresenter<in T> {
/*該方法可以獲取到View實例對象*/
fun attachView(view: T)
/*釋放View對象的引用锈津,gc才能回收View*/
fun detachView()
}
interface BaseView {
//view的一些共有方法
fun showError(e: Throwable)
fun complete()
}
}
- 3暮顺、一個實現(xiàn)了View接口充當(dāng)V層的Activity
/**
* 模擬登錄的view層
*/
class MainActivity : BaseActivity<LoginContract.View,LoginContract.Presenter>(),LoginContract.View {
override fun err(code: Int, message: String) {
ToastUtils.showShort("錯誤碼:$code\n錯誤信息:$message")
}
override fun loginSus(loginModel: LoginModel) {
disMissLoading()
if (loginModel.State) {
ToastUtils.showShort("登錄成功")
}else{
ToastUtils.showShort(loginModel.Message)
}
}
override fun showError(e: Throwable) {
disMissLoading()
}
override fun complete() {
}
override fun getLayoutId(): Int = R.layout.activity_main
override fun configView() {
login.setOnClickListener({
showLoading()
mPresenter!!.login("18381309101","1111","") })
}
override fun initData() {
}
override fun initPresenter(): LoginContract.Presenter = LoginPresenter()
}
- 實現(xiàn)Presenter,充當(dāng)P層的一個登錄的Presenter
class LoginPresenter : RxPresenter<LoginContract.View>(), LoginContract.Presenter {
/**
* 登錄
* @param telephoneNumber
* @param verificationCode
* @param token
*/
override fun login(telephoneNumber: String, verificationCode: String, token: String) {
val subscription = HttpManager.getWorkHttpService()!!.signIn(telephoneNumber, verificationCode, token)
.compose(ScheduleTransformer.instance)
.subscribe(object : ObserverImp<Any>() {
override fun onErr(errCode: Int, str: String) {
mView!!.err(errCode,str)
}
override fun doNext(loginModel: Any) {
mView!!.loginSus(loginModel as LoginModel)
}
})
addSubscribe(subscription)
}
}
到這里mvp構(gòu)建就已經(jīng)完成了响驴,但是那些日常操作:統(tǒng)一錯誤處理透且、文件進(jìn)度監(jiān)聽、Retorfit的自定義攔截器豁鲤、內(nèi)存泄漏處理秽誊、Retrofit自定義轉(zhuǎn)換器等等還需要完成。琳骡。锅论。
一些重要的代碼:
- 統(tǒng)一錯誤處理
override fun onError(e: Throwable) {
var e = e
var throwable = e
//獲取最根源的異常
while (throwable.cause != null) {
if (e is HttpException) {
break
}
e = throwable
throwable = throwable.cause!!
}
if (e is HttpException) {
when (e.code()) {
UNAUTHORIZED -> onErr(UNAUTHORIZED, "")
FORBIDDEN -> onErr(FORBIDDEN, "權(quán)限錯誤") //權(quán)限錯誤,需要實現(xiàn)
NOT_FOUND -> onErr(NOT_FOUND, "")
REQUEST_TIMEOUT -> onErr(REQUEST_TIMEOUT, "")
GATEWAY_TIMEOUT -> onErr(GATEWAY_TIMEOUT, "")
INTERNAL_SERVER_ERROR -> onErr(INTERNAL_SERVER_ERROR, "")
BAD_GATEWAY -> onErr(BAD_GATEWAY, "")
SERVICE_UNAVAILABLE -> onErr(SERVICE_UNAVAILABLE, "")
else -> onErr(ERR_CODE_NET, "")
}
} else if (e is SocketTimeoutException) {
onErr(GATEWAY_TIMEOUT, "請求超時!")
} else if (e is UnknownHostException) {
onErr(ERR_CODE_NET, "網(wǎng)絡(luò)連接失敗!")
} else {
onErr(ERR_CODE_UNKNOWN, "未知錯誤!")
}
}
- 2楣号、文件進(jìn)度監(jiān)聽
override fun read(sink: Buffer, byteCount: Long): Long {
try {
val bytesRead = super.read(sink, byteCount)
if (totalBytesRead == 0L && listener != null) {
listener.DLoadStart()
}
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
if (null != listener) {
if (bytesRead == -1L) {
listener.DLoadSuccess()
}
if ((totalBytesRead * 100L / responseBody.contentLength()).toInt() > lastPro) {
listener.DLoadProgress((totalBytesRead * 100L / responseBody.contentLength()).toInt())
}
lastPro = (totalBytesRead * 100L / responseBody.contentLength()).toInt()
}
return bytesRead
} catch (e: IOException) {
listener?.DLoadFail()
}
return 0L
}
- 3最易、自定義文件下載攔截器
/**
* 文件下載攔截器
*/
class DownLoadInterceptor(private val listener: DLProListener) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val originalResponse = chain.proceed(chain.request())
return originalResponse.newBuilder()
.body(FileResBody(originalResponse.body(), listener))
.build()
}
}
- 4、內(nèi)存泄漏處理竖席,釋放對activity 的引用
override fun attachView(view: T) {
this.mView = view
}
override fun detachView() {
this.mView = null
unSubscribe()
}
- 5耘纱、自定義單例的Retrofit轉(zhuǎn)換器
/**
* 單例實現(xiàn)的轉(zhuǎn)換器,統(tǒng)一指定每一個接口請求實在io線程毕荐,回調(diào)在ui線程
*/
class ScheduleTransformer<T>
/**
* 私有的構(gòu)造函數(shù)
*/
private constructor() : Observable.Transformer<T, T> {
/**
*懶加載內(nèi)部單例
*/
private object TransformerHolder {
private val instance: ScheduleTransformer<Any>? = null
fun getInstance(): ScheduleTransformer<Any> {
return instance ?: ScheduleTransformer<Any>()
}
}
override fun call(tObservable: Observable<T>): Observable<T> {
return tObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
/**
* 伴生
*/
companion object {
val instance: ScheduleTransformer<Any>
get() = TransformerHolder.getInstance()
}
}
該架構(gòu)用起來十分方便和靈活束析,里面還有常用的一些基類和處理類,已經(jīng)在正式項目中使用憎亚,請大膽嘗試员寇,Github地址:實戰(zhàn)MVP架構(gòu),包含JAVA版本第美,歡迎打星鼓勵蝶锋!