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

3,440 阅读4分钟

前言

学习了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(三)

下次再见