Android MVP使用Kotlin封装

Android应用开发项目默认是MVC架构的,对于古老的Java桌面开发来说,已经是一大进步了。但随着项目的复杂度越来越高,Activity明显hold不住日益膨胀的逻辑代码。各种回调也让Activity的生命周期回收处于失控边缘,导致各种难以排查的内存泄漏。

于是,MVP应运而生,甚至后来衍生了更加激进的MVVM,以及其他AAC等形形色色的架构。

正所谓大道至简,返璞归真。MVP的存在自然有其可取之处,MVP比MVVM更能把控复杂的项目,虽然Presenter会持有Activity的引用(引入androidx.lifecycle.ViewModel后已经完美解决内存泄漏的问题),但模块独立,且回调非常方便。

这里提到MVVM,虽然不用特殊处理就可以解决内存泄漏的问题,但回调需要借助LiveData,且在xml里写代码,让我想起了当年被JSP支配的恐惧,可能早期的MVVM也会遇到断点调试的问题。

这里介绍一下封装的这个MVP框架使用,无需考虑内存泄漏的问题,且使用非常方便,可以自行选择是否启用屏幕适配、高刷新率等(本框架在Kotlin项目中使用可以把简洁优雅体现得淋漓尽致,Kotlin也是方向,今后移动应用的项目肯定以Kotlin为主了)。

一、项目配置

1.在build.gradle(app)同目录新建mvp.gradle文件

mvp.gradle的内容为:

dependencies {
    // 使用 retrofit + rx + gson 实现网络请求与解析
    def retrofit_version = '2.9.0'
    // 使用 retrofit + rx + gson 实现网络请求与解析---->
    api "com.squareup.retrofit2:retrofit:$retrofit_version"
    // 衔接 retrofit 和 rxjava
    api 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
    // 衔接 retrofit 和 gson
    api 'com.squareup.retrofit2:converter-gson:2.8.1'

    // 此处一定要注意使用RxJava2的版本,和Retrofit配合
    api 'io.reactivex.rxjava2:rxjava:2.2.17'
    api 'io.reactivex.rxjava2:rxandroid:2.1.1'

    // GSON
    api 'com.google.code.gson:gson:2.8.6'

    // 打印网络请求日志框架
    api "com.squareup.okhttp3:logging-interceptor:3.6.0"

    // 通用设计
    api "androidx.activity:activity:1.2.0-beta01"
    // ViewBinding
    api 'androidx.databinding:viewbinding:4.2.0@aar'

    // kotlin
    api "androidx.lifecycle:lifecycle-extensions:2.2.0"
    api "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
    api "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
    api "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
    api "androidx.preference:preference-ktx:1.1.1"
}

2.在build.gradle(app)顶部添加:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}
apply from: 'mvp.gradle'

3.build.gradle(app)的android节点增加:

// 开启ViewBinding
buildFeatures{
    viewBinding = true
}

4.在gradle.properties文件增加:

android.useAndroidX=true
android.enableJetifier=true

5.把mvp.aar放到app/libs文件夹下,并在build.gradle(app)添加依赖:

implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])

以上就是所有的集成步骤了,看起来似乎有5步,其实每一步都非常简单。下面介绍部分常用功能使用:

二、基本使用

布局activity_test.xml:


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
FrameLayout>

Activity:

class TestActivity: BaseActivity<ActivityTestBinding>() {
    override fun onBindingInflate() = ActivityTestBinding.inflate(layoutInflater)

    /**
     * 延迟加载一个Presenter
     */
    private val mPresenter: TestPresenter by lazy()

    override fun onInit() {
        // 开始执行耗时任务
        mPresenter.runTimeConsumeTask()
    }

    fun onTaskResult(result: String?) {
        // 展示结果
        binding.tvTest.text = result
    }
}

温馨提示:activity添加android:configChanges="locale|orientation|screenSize|keyboardHidden"可以避免很多生命周期造成的问题,注意要指定主题继承自Theme.AppCompat哦。

Presenter:

class TestPresenter: BasePresenter<TestActivity>() {
    /**
     * 运行耗时任务
     */
    fun runTimeConsumeTask() {
        launchTask(
            {
                // 5秒耗时任务
                delay(5_000L)
                // 返回结果
                "success"
            }, onSuccess = {
                // 这里已经是主线程,回调
                view?.onTaskResult(it)
            }
        )
    }
}

不用关心内存泄漏,一个Activity可以多开Presenter,且Presenter可以在多个模块流转,只要把BasePresenter的泛型换成继承于IView的接口即可。

三、网络请求

1.网络管理者

/**
 * 网络请求封装
 */
object ApiManager {
    /**
     * 默认的网络请求
     * BASE_URL需要以/结尾,如:https://www.baidu.com/
     */
    val default = RetrofitManager.getDefault().createApiService(BuildConfig.BASE_URL, ApiService::class.java)
}

2.网络接口

/**
 * 请求接口
 */
interface ApiService {
    /**
     * 获取验证码接口
     */
    @GET("prod-api/api/login/smsCode")
    suspend fun sendSmsCode(@Query("phone") phone: String): BaseResponse<Any>
}

3.BaseResponse

/**
 * 统一返回封装
 */
@Keep
class BaseResponse<T> : IResponse {
    /**
     * 返回状态代码
     */
    var code = NetConstants.SUCCESS

    /**
     * 消息
     */
    var msg: String? = null

    /**
     * 返回的实体数据
     */
    var data: T? = null
    
    /**
     * 校验返回信息,如果没有错误也没有过期就返回true,如果有错误则抛出异常即可
     * 防止有些不需要使用到结果的接口不断提交失败,及时发现隐藏的重大错误如登录过期等
     */
    override fun valid(): Boolean {
        return true
    }
}

4.在Presenter中使用

/**
 * 发送验证码
 */
fun sendSms(phone: String) {
    launchTask(
        {
            ApiManager.default.sendSmsCode(phone)
        }, onResult = { success, result, code, message ->
            if (success) {
                ToastUtils.showShort("发送成功")
                view?.onSmsSuccess()
            } else {
                ToastUtils.showShort("发送失败:$message")
            }
        }
    )
}

四、实用扩展

1.申请权限

requestPermissions(Manifest.permission.CAMERA) { grant, temp ->
    if (grant) {
        ToastTools.showShort("granted")
    } else {
        ToastTools.showShort("deny")
    }
}

2.启动Activity

openActivity<MainActivity>()

3.启用高刷新率

enableHighRefreshRate()

4.startActivityForResult

openActivityForResult(intent, object : ActivityResult(){
    override fun onResultOK(backIntent: Intent?) {
        super.onResultOK(backIntent)
        ...
    }
})

5.防止快速点击

binding.btnTest.setSingleClick {
	...
}

6.扩大点击范围

binding.ivClose.expandClick()

7.设置Activity动画左进右出

MvpManager.getInstance().activityAnimation(SlideAnimation())

8.网络请求信息

RetrofitManager.getDefault()
    .addInterceptors(ChuckInterceptor(this)) // 在通知栏显示网络请求(部分手机需要手动开启通知权限)
    .printLog(true) // 是否在Logcat打印网络请求

其他屏幕适配、dp和sp转px等等

五、例子下载

参考我开源的传输工具:https://gitee.com/miekir/easy-transfer

你可能感兴趣的:(Android,Kotlin,android,架构,kotlin)