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