Kotlin的魔能機甲——KtArmor(一)

hyzhan43發表於2019-07-24

前言

學習了Kotlin有一段時間了, 每次寫專案/Demo的時候, 總是用到網路請求MVPMVVM常用工具類通用自定義View, 索性把這些整合到一起, 搭成一個Android的腳手架——KtArmor.

什麼是KtArmor ?

KtArmor 寓意著 為Android 賦予戰鬥裝甲, 方便開發者快速進行Android 開發。節約開發者開發時間。為了滿足開發者需求, 我整合了兩個分支, 分別對應著 MVP, MVVM.

  • MVP分支
    • 架構模式: MVP + Kotlin
    • 網路請求: Retrofit + Okhttp + Coroutine + RxJava
    • 功能
      • 基本BaseActivityBaseFragmentToolbarActivity封裝
      • MVP框架封裝 MvpActivityMvpFragmentBasePresenterBaseModel封裝
      • 網路請求封裝 BaseOkHttpClientBaseRetrofitRetrofitFactory
      • 常用控制元件PlaceHolderView(佔位佈局)LoadingView(載入框)
      • 常用擴充套件封裝(SharedPreferencesStartActivityLogToast(不重複顯示))等
      • MVP程式碼模板(ActivityPresenterContractModel)生成外掛
      • ....
  • MVVM分支
    架構模式: MVVM+ Androidx + Kotlin + LiveData + ViewModel
    網路請求: Coroutines + Retrofit + Okhttp

MVP框架引入

先在 build.gradle(Project:XXXX) 的 repositories 新增:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}
複製程式碼

然後在 build.gradle(Module:app) 的 dependencies 新增:

implementation 'com.github.hyzhan43:KtArmor:mvp-1.0.4'  // 根據github 引入最新版本即可
複製程式碼

快速上手

在Application 中初始化KtArmor 框架。新建一個application 類, 如 BaseApplication, 在 BaseApplication 中, 呼叫KtArmor的 init 方法, 進行初始化, 引數如下:

  • 第一個引數是application,
  • 第二個引數是對應RetrofitConfig 配置。
class BaseApplication: Application(){

    override fun onCreate() {
        super.onCreate()

        // 初始化KtArmor
        KtArmor.init(this, MyRetrofitConfig())
    }
}
複製程式碼

再新建一個 RetrofitConfig 配置類, 繼承 BaseRetrofitConfig. 並複寫 baseUrl 屬性, 新增自己的 baseUrl。

class MyRetrofitConfig : BaseRetrofitConfig() {

    override val baseUrl: String
        get() = API.BASE_URL
}
複製程式碼

這樣你就建立好了一個擁有Kotlin + Retrofit + Okhttp + Coroutine專案了。然後就可以愉快編寫自己的業務程式碼了(●'◡'●)

Login 示例

1、LoginContract

我們先從簡單登入流程來熟悉一下KtArmor。首先編寫 LoginContract, 程式碼如下:

interface LoginContract {

    interface View : BaseContract.View {
        fun accountEmpty(msg: Int)
        fun passwordEmpty(msg: Int)
        fun loginSuc(loginRsp: LoginRsp)
    }

    interface Presenter : BaseContract.Presenter {
        fun login(account: String, password: String)
    }
}
複製程式碼

2、LoginActivity

然後新建一個LoginActivity, 繼承 MvpActivity 並傳遞對應 LoginContract.Presenter 泛型,實現 LoginContract.View 介面, 程式碼如下:

class LoginActivity : MvpActivity<LoginContract.Presenter>(), LoginContract.View {

    override fun getLayoutId(): Int = R.layout.activity_login

    override fun bindPresenter(): LoginContract.Presenter = LoginPresenter(this)

    override fun initListener() {
        super.initListener()

        mBtnLogin.setOnClickListener {
            mTilAccount.isErrorEnabled = false
            mTilPassword.isErrorEnabled = false
            
            // 發起登入請求
            presenter.login(mEtAccount.str(), mEtPassword.str())
        }
    }

    override fun accountEmpty(msg: Int) {
        mTilAccount.isErrorEnabled = true
        mTilAccount.requestFocus()
        mTilAccount.error = getString(msg)
    }

    override fun passwordEmpty(msg: Int) {
        mTilPassword.isErrorEnabled = true
        mTilPassword.requestFocus()
        mTilPassword.error = getString(msg)
    }

    override fun loginSuc(loginRsp: LoginRsp) {
        toast("登陸成功!")
    }
}
複製程式碼
  • bindPresenter 方法返回 LoginPresenter 例項。
  • getLayoutId方法 返回LoginActivity 佈局id。

這裡 activity_login 裡面是簡單的 TextInputEditText,呼叫presenter, 發起登入請求。傳遞賬號和密碼。其中 str() 為 TextView 擴充套件方法。

  • str() 為擴充套件方法
// 獲取text內容
fun TextView.str(): String {
    return this.text.toString()
}
複製程式碼

3、LoginPresenter

然後我們再看看對應 LoginPresenter 實現, 繼承 BasePresenter,並傳遞對應 LoginContract.View

class LoginPresenter(view: LoginContract.View) : BasePresenter<LoginContract.View>(view), LoginContract.Presenter {

    override fun login(account: String, password: String) {

        if (account.isEmpty()) {
            view?.accountEmpty(R.string.account_empty)
            return
        }

        if (password.isEmpty()) {
            view?.passwordEmpty(R.string.password_empty)
            return
        }

        launchUI({
            view?.showLoading()
            val response = LoginModel.login(account, password)

            // 正常返回結果處理
            if (response.isSuccess()) {
                response.data?.let { view?.loginSuc(it) }
            } else {
                view?.showError(response.errorMsg)
            }
        }, {
            // TODO 異常處理
        })
    }
}
複製程式碼

在這裡, 我們採用協程來實現切換執行緒操作。在 launchUI() 方法裡面啟動了一個 UI 協程,在這裡呼叫 LoginModel 真正發起網路請求操作。

4、LoginModel

object LoginModel : BaseModel() {

    suspend fun login(account: String, password: String): BaseResponse<LoginRsp> {
        return launchIO { ApiManager.apiService.loginAsync(account, password).await() }
    }
}
複製程式碼

同樣,LoginModel 需要繼承 BaseModel(),並呼叫 launchIO 進行執行緒切換。切換到 IO執行緒 通過ApiManager.apiService 發起網路請求。然後呼叫 await() 返回結果。這裡 ApiService 通過 RetrofitFactory建立, 傳入 Service class。

object ApiManager {

    val apiService by lazy {
        RetrofitFactory.instance.create(ApiService::class.java)
    }
}
複製程式碼
interface ApiService {

    @POST(API.LOGIN)
    fun loginAsync(@Query("username") username: String,
                   @Query("password") password: String): Deferred<BaseResponse<LoginRsp>>
}
複製程式碼

以上就是登入的全過程。看到這裡,編寫一個簡單Login功能需要新建四個類,有點麻煩。有沒有更便捷的方法的。那肯定!KtArmor 框架還有與之對應 KtArmor-MVP 外掛,幫助開發者快速生成對應模板程式碼(ActivityPresenterContractModel)。

未完待續

這是KtArmor開篇的第一篇。大概講解了KtArmor基本用法。後續會詳細講解框架的使用、以及外掛的使用。至於KtArmor-MVVM 版目前還在測試階段。後續也會陸續更新。敬請期待吧!
著急的小夥伴可以直接檢視下文原始碼~

最後

KtArmor 框架是一款小而美的框架,也是我個人經驗的積累, 總結。如有不妥, 望各位大佬指出。歡迎大家 pr交易, 一起交流學習。

KtArmor-MVP 原始碼傳送門

Kotlin的魔能機甲——KtArmor外掛篇(二)

Kotlin的魔能機甲——KtArmor(三)

下次再見

相關文章