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

前言

繼上篇說到, KtArmor-MVP的插件使用。咱們能夠快速建立基本的模板代碼,可是在編寫業務代碼時候,不熟悉KtArmor-MVP框架, 不知其然,沒法駕馭這個魔能機甲 。因此這篇我先從BaseActivity 開始提及,介紹KtArmor—MVP 的用法,「深刻源碼」解析,帶你走進 KtArmor-MVP。android

Activity

KtArmor-MVP 框架主要包含3個主要的Activitygit

  • BaseActivity
  • ToolbarActivity
  • MvpActivity

它們之間繼承關係以下:github

MvpActivity > ToolbarActivity > BaseActivity網絡

而後咱們來看看他們具體的實現框架

BaseActivity

abstract class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 在界面未初始化以前調用的初始化窗口
        initWidows()

        if (initArgs(intent.extras)) {
            setContentView(getLayoutId())

            initBefore()
            initView()
            initListener()
            initData()
        } else {
            finish()
        }
    }

    open fun initArgs(bundle: Bundle?): Boolean = true

    open fun initWidows() {}

    abstract fun getLayoutId(): Int

    open fun initBefore() {}

    open fun initView() {}

    open fun initListener() {}

    open fun initData() {}
}
複製代碼

BaseActivity 基本的模板結構,定義了基本的Activity 初始化的方法。能夠繼承BaseActivity,複寫對應方法進行擴展。下面是方法具體描述:ide

  • initWidows: 在界面(setContentView)未初始化以前調用的初始化窗口方法
  • initArgs: 初始化界面參數方法(Activity 之間跳轉傳遞參數), 該方法 默認返回 True, 顯示Activity, 不然返回False, 不顯示Activity。
  • getLayoutId:初始化 Activity 的 layout 佈局
  • initBeforeinitView() 以前, setContentView()方法 以後的初始化方法。
  • initView:初始化控件view 方法.
  • initListener:初始化 控件view 相關 listener 方法。
  • initData:初始化數據方法

能夠適用於 APP 啓動頁面簡單展現頁面等, 不涉及到Presenter 的 Activity函數

ToolbarActivity

abstract class ToolbarActivity : BaseActivity() {

    var toolbarTitle: String = ""
        set(value) {
            field = value
            supportActionBar?.title = value
        }

    override fun initView() {
        super.initView()
        initToolbar()
    }

    /** * Toolbar id must be toolbar */
    private fun initToolbar() {
        findViewById<Toolbar>(R.id.toolbar)?.let { toolbar ->
            setSupportActionBar(toolbar)
            supportActionBar?.let {
                it.setDisplayHomeAsUpEnabled(true)
                it.setDisplayShowHomeEnabled(true)
            }
        }
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when (item?.itemId) {
            //將滑動菜單顯示出來
            android.R.id.home -> {
                finish()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }
}
複製代碼

ToolbarActivity 繼承 BaseActivity, 方便於顯示 Toolbar,在項目中挺經常使用的,因此就封裝這個Toolbar基本用法。佈局

  • Toolbar title 的顯示
  • Toolbar 返回鍵(android.R.id.home)的關閉操做。
  • toolbarTitle: 能夠更改 toolbar 對應的 title

在 Activity 的 xml 引入 Toolbar控件, 而且 id 必須爲 toolbar,不然不會調用 initToolbar初始化方法 !!!post

MvpActivity

abstract class MvpActivity<P : BaseContract.Presenter> : ToolbarActivity(), BaseContract.View {

    lateinit var presenter: P

    override fun initBefore() {
        presenter = bindPresenter()
    }

    abstract fun bindPresenter(): P

    override fun showError(@StringRes msgRes: Int) {
        showError(getString(msgRes))
    }

    override fun showError(msg: String) {
        toast(msg)
        hideLoading()
    }

    override fun showLoading() {}

    override fun hideLoading() {}

    override fun onDestroy() {
        super.onDestroy()

        if (::presenter.isInitialized) {
            presenter.detachView()
        }
    }
}
複製代碼

MvpActivity 一樣是繼承ToolbarActivity, 實現了基本 BaseContract.View, 管理着 Presenter 生命週期。子類須要實現 bindPresenter()方法,傳遞對應的 Presenter。 而後就能夠調用 Presenter 進行後續的操做。測試

  • 封裝了 Presenter 初始化,銷燬
  • 複寫 showError(), showLoading(), hideLoading() 等方法。(簡單toast 了)

::presenter.isInitialized 意思是判斷 Presenter 是否懶加載初始化, 防止未初始化,拋異常。
後續可能會經過泛型T, 反射生成Presenter,減小重複操做

Fragment

BaseFragmentMvpFragment 的實現和 Activity 實現殊途同歸,這裏就不過多介紹了~

Presenter

abstract class BasePresenter<V : BaseContract.View>(view: V) : BaseContract.Presenter{

    val view: V?
        get() = mViewRef.get()

    // View 接口類型的弱引用
    private var mViewRef = WeakReference(view)

    val presenterScope: CoroutineScope by lazy {
        CoroutineScope(Dispatchers.Main + Job())
    }

    fun launchUI(block: suspend CoroutineScope.() -> Unit, error: ((Throwable) -> Unit)? = null) {
        presenterScope.launch {
            tryCatch({
                block()
            }, {
                error?.invoke(it) ?: view?.showError(it.toString())
            })
        }
    }

    override fun detachView() {
        mViewRef.clear()
        // 取消掉 presenterScope建立的全部協程和其子協程。
        presenterScope.cancel()
    }
}
複製代碼
  • 綁定 View 的初始化, 銷燬, 採用 弱引用方式, 防止內存泄露。
  • launchUI 封裝 協程,切換到 Main線程方法,並進行tryCatch 捕獲異常。
  • presenterScope 的銷燬,綁定到 對應 UI界面 的 onDestory方法,防止內存泄露。

Model

abstract class BaseModel {

    suspend fun <R> launchIO(block: suspend CoroutineScope.() -> R) = withContext(Dispatchers.IO) {
        block()
    }
}
複製代碼

BaseModel 相對簡單, 封裝了協程切換 IO 線程的操做.

後續可能添加相關 DB 相關操做

Retrofit

class MyRetrofitConfig : BaseRetrofitConfig() {

    override val baseUrl: String
        get() = API.BASE_URL
        
    override val readTimeOut: Long
        get() = //TODO

    override val writeTimeOut: Long
        get() = //TODO

    override val connectTimeOut: Long
        get() = //TODO

    override fun initRetrofit(): Retrofit {
    
        // 默認實現 BaseRetrofit.init()
        return Retrofit.Builder()
            .baseUrl(KtArmor.retrofit.baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .client(KtArmor.retrofit.initOkHttpClient())
            .build()
            
    }

    override fun initOkHttpClient(): OkHttpClient {
        // 能夠傳遞 Interceptor 進行網絡請求攔截
        return BaseOkHttpClient.init(TokenInterceptor.create())
    }
}
複製代碼

Retrofit 相關網絡操做, 能夠繼承BaseRetrofitConfig類, 在這裏能夠配置本身的參數進行擴展, 相關參數以下:

  • baseUrl: 網絡請求的 baseUrl
  • initRetrofit: 爲初始化 Retrofit方法,能夠返回自定Retrofit
    • 默認實現BaseRetrofit.init()
  • initOkHttpClient 初始化 OkHttp的方法,能夠返回自定Okhttp
    • 默認實現BaseOkHttpClient.init()
    • init()方法能夠傳入對應的 Interceptor, 進行攔截網絡操做.
    • readTimeOut, writeTimeOut, connectTimeOut 能夠複寫網絡鏈接超時屬性

SharedPreferences

KtArmor-MVP 經過代理方式,封裝了 SharedPreferences基本操做.

  • 經過定義一個變量,並經過 by Preference 代理
  • 傳遞 Sp的 key 和對應的 defaultValue

例如

var account by Preference(Key.ACCOUNT, "")
複製代碼

定義了一個 account 變量,傳遞對應Sp 存儲的key,和默認值 「」 (空串, 說明account 是 String 類型)
而後直接當正常變量使用, 以下直接賦值 就能夠修改 Sp 中keyKey.ACCOUNT 的值了。代碼以下

account = "123"
複製代碼

tryCatch

// 傳統的 tryCatch
        try{
            // TODO
        }catch (e: Exception){
            // TODO 
        }
        
        // KtArmor-MVP 擴展
        tryCatch({
            // TODO
        })

        // KtArmor-MVP 擴展
        tryCatch({
            // TODO
        }, {
            // TODO
        })
複製代碼

擴展函數

  • Toast 擴展, 不重複showToast,屢次點擊會替換
    • 支持:Context,Activity, Fragment 擴展
    • 擴展參數string (或 @StringRes ), duration
    • 全局 Toast, 經過 Toasts.show(xx)
  • sp, dp 相互轉化
    • 支持:Float to Float, Int to Int
  • Log
    • 支持:string.showLog()
    • 示例
      "我是Log".showLog()
      複製代碼
  • R.color.xxx -> Color IntR.drawable.xxx -> Drawable 擴展
    • 支持:Context,View 下擴展
    • 示例:
      val defaultColor: Int = context.getColorRef(R.color.xxx)
      val defaultDrawable: Drawable? = context.getDrawableRef(R.drawable.xxx)
      複製代碼
  • startActivity 參照 anko 的 startActivity (fuzhi)
    • 支持:Context, Fragment 下擴展
    • 示例:
      startActivity<XXXActivity>(key to value)
      複製代碼
  • View相關擴展
    • TextView 擴展
      • 示例
        // 直接獲取 TextView 的 text 值
        mTvAccount.str()
        複製代碼
    • View 顯示隱藏擴展
      • 示例
        mIvImage.visible()
        複製代碼
    • 顯示,關閉軟鍵盤擴展
      • 支持:View, Activity
      • 示例
        activity.hideKeyboard()
        複製代碼
  • ...

最後

以上是KtArmor-MVP 的所有內容,後續框架有更新,也會更新相關文檔。

仍是那句話,KtArmor-MVP 封裝了基本 MVP結構的框架,是一款小而美的框架,麻雀雖小五章俱全。封裝了基礎的功能,小的項目,或者測試項目能夠直接拿來用,省時省力。但願你們喜歡~

最後,如有不妥,望小夥伴們指出。

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

Kotlin的魔能機甲——KtArmor插件篇(二)

KtArmor-MVP 源碼傳送門

感謝閱讀,下次再見

相關文章
相關標籤/搜索