用RxJava+Retrofit+OKHttp封装的一个Kotlin版本的MVP框架,利用Koin管理类的生命周期

3,228 阅读5分钟

kotlin-mvp

技术要点

1. RxJava+Retrofit+OkHttp实现链式http请求

2. 封装基类:BaseActivity、BasePresenterActivity、BaseFragment、BasePresenterFragment、RecycleAdapter、BasePresenter

3. 封装工具扩展类:CalendarExt、ContextExt、DateExt、EditTextExt、GsonExt、RxJavaExt、StringExt

4. 引入LifeCycle,将Presenter和Activity的生命周期绑定在一起

5. 将在Application中初始化移至到ContentProvider中,从而不用封装BaseApplication

6. AOP(面向切面)封装注解:TimeLog、ClickGap、MethodLog

7. APT(编译时注解)封装注解:OnClickFirstDrawable、OnClickFirstText、OnClickSecondDrawable、OnClickSecondText、Prefs、PrefsField、StatusBar

8. Koin对类的生命周期做一个管理

最低兼容:21

Gradle

1. 在Project的build.gradle中添加

allprojects {
     repositories {
 	    maven { url 'https://jitpack.io' }
     }
 }

2. AspectJX的使用请参考官方文档

dependencies {
    classpath 'com.github.franticn:gradle_plugin_android_aspectjx:2.0.10'
}

3. 在app的build.gradle的添加

apply plugin: 'kotlin-kapt' // 使用 kapt 注解处理工具

apply plugin: 'android-aspectjx'

aspectjx {
    exclude 'versions.9.module-info.class'
    exclude 'module-info.class' 
}

4. 添加依赖

last_version = "2.0.3"
implementation "com.github.catch-pig.kotlin-mvp:mvp:last_version"
kapt "com.github.catch-pig.kotlin-mvp:compiler:last_version"

使用

1. 在需要使用状态栏、标题栏、加载动画的主题中配置全局参数:

属性类型必须默认说明
title_bar_back_iconDrawableRes标题栏的返回图标
title_bar_backgroundColorRes标题栏的背景色
title_bar_text_colorColorRes标题栏的文字颜色
title_bar_show_linebooleanfalse标题栏的下方的线条是否显示
loading_view_colorColorResloading动画颜色
loading_view_backgroundColorResloading动画背景色
recycle_view_empty_layoutLayoutResemptyLayout列表空页面

使用示例:

```
<style name="AppThemeBarStyle" parent="Theme.AppCompat.Light.NoActionBar">
    <!--全局标题栏和状态栏配置-->
    <item name="title_bar_background">@color/colorPrimary</item>
    <item name="title_bar_back_icon">@drawable/back</item>
    <item name="title_bar_text_color">@color/white</item>
    <item name="title_bar_show_line">false</item>
    <item name="loading_view_color">@color/colorAccent</item>
    <item name="loading_view_background">@color/white</item>
    <!--全局标题栏和状态栏配置-->
</style>
```

2. Activity

  • 使用MVP的继承BasePresenterActivity
  • 不使用MVP的继承BaseActivity

3. Fragment

  • 使用MVP的继承BasePresenterFragment
  • 不使用MVP的继承BaseFragment

4. 如果使用RecycleView的时候,Adapter可以继承RecycleAdapter来使用

在app的build.gradle的android下添加如下代码:

//启用实验性功能
androidExtensions {
    experimental = true
}

只需要实现以下两个方法

class UserAdapter(iPageControl: IPageControl):RecyclerAdapter<User>(iPageControl) {
    override fun layoutId(): Int {
        return R.layout.item_user
    }

    override fun bindViewHolder(holder: CommonViewHolder, m: User, position: Int) {
        //使用的experimental之后,可以直接holder.控件ID,不需要holder.itemView.控件ID
        holder.name.text = m.name
    }
}

5. 注解使用

5.1 Title-标题

属性类型必须默认说明
valueStringRes标题内容
backgroundColorColorRes全局标题背景色标题背景色
textColorColorRes全局标题文字颜色标题文字颜色
backIconDrawableRes全局标题返回按钮图标标题返回按钮图标

5.2 OnClickFirstDrawable-标题上第一个图标按钮的点击事件

属性类型必须默认说明
valueStringRes按钮图片内容

5.3 OnClickFirstText-标题上第一个文字按钮的点击事件

属性类型必须默认说明
valueStringRes按钮文字内容

5.4 OnClickSecondDrawable-标题上第二个图标按钮的点击事件

属性类型必须默认说明
valueStringRes按钮图片内容

5.5 OnClickSecondText-标题上第二个文字按钮的点击事件

属性类型必须默认说明
valueStringRes按钮文字内容

5.6 StatusBar-状态栏

属性类型必须默认说明
hidebooleanfalse隐藏状态栏
enabledbooleanfalse状态栏是否可用
transparentbooleanfalse状态栏透明

5.7 TimeLog-打印方法和构造方法执行的时间

属性类型必须默认说明
valueLEVELLEVEL.D日志等级

5.8 ClickGap-重复点击延时

属性类型必须默认说明
valueLong800毫秒重复点击间隔

5.9 Prefs-SharedPreferences注解生成器

属性类型必须默认说明
valueString""别名
modePrefsModePrefsMode.MODE_PRIVATE模式,对应PreferencesMode

5.10 PrefsField-SharedPreferences字段注解

属性类型必须默认说明
valueString""字段别名,如果为空则取修饰字段的参数名称

5.11 MethodLog-打印方法和构造方法以及参数的值

属性类型必须默认说明
valueLEVELLEVEL.D日志等级

6. 刷新分页

使用RefreshLayoutWrapper+RecyclerAdapter控件实现刷新功能

  • RefreshLayoutWrapper继承于SmartRefreshLayout,具体使用请看SmartRefreshLayout官方文档,默认每页数据量为16,如果想修改每页数据量,可使用如下方法更改:

    RefreshLayoutWrapper.pageSize = 16
    
  • RefreshLayoutWrapper实现了IPageControl,可以通过调用接口内的方法类获取刷新控件的状态和更改状态

    //获取刷新的状态
    iPageControl.getRefreshStatus()
    
  • RecyclerAdapter在实例化的时候传入IPageControl, 获取数据成功之后,只需要调用autoUpdateList(list)方法, 可以自动RefreshLayoutWrapper页码和刷新状态变化

  • 数据更新失败可以调用RecyclerAdapter.updateFailed()

  • 获取每页的数据量和下一页的页码,可以调用一下方法

    //每页的数据量
    RecyclerAdapter.pageSize = 16
    //下一页的页码
    RecyclerAdapter.nextPageIndex = 1
    

7. 文件下载器(DownloadManager))

  • 在application的onCreate中startKoin,加入downloadModule

     startKoin {
     modules(downloadModule)
     }
    
    • downloadModule

      /**
       * 下载相关类的初始化管理
       */
      const val NAMED_DOWNLOAD = "download"
      val downloadModule = module {
          single(named(NAMED_DOWNLOAD)) { (downloadProgressListener: DownloadProgressListener)->
              DownloadInterceptor(downloadProgressListener)
          } bind Interceptor::class
      
          single(named(NAMED_DOWNLOAD)) { (downloadProgressListener: DownloadProgressListener,         timeout:Long)->
              OkHttpClient
                      .Builder()
                      .connectTimeout(timeout, TimeUnit.SECONDS)
                      .addInterceptor(get<Interceptor>())
                      .addInterceptor(get<Interceptor>(named(NAMED_DOWNLOAD)){         parametersOf(downloadProgressListener)})
                      .build()
          }
      
          single(named(NAMED_DOWNLOAD)) { (baseUrl:String,timeout:Long,downloadProgressListener:         DownloadProgressListener)->
              Retrofit
                      .Builder()
                      .baseUrl(baseUrl)
                      .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                      .client(get(named(NAMED_DOWNLOAD)){ parametersOf(downloadProgressListener,timeout)})
                      .addConverterFactory(GsonConverterFactory.create())
                      .build()
                      .create(DownloadService::class.java)
          }
      
          single {
              DownloadManager()
          }
      }
      
  • 调用下载方法download(DownloadInfo,DownloadCallback)

    val downloadInfo = DownloadInfo("https://wanandroid.com/","blogimgs/2d120094-e1ee-47fb-a155-6eb4ca49d01f.apk")
    model.download(downloadInfo,object : DownloadCallback {
        override fun onStart() {
    
        }
    
        override fun onSuccess(path: String) {
            view.activity().installApk(path)
        }
    
        override fun onComplete() {
        }
    
        override fun onProgress(readLength: Long, countLength: Long) {
            view.setDownloadProgress((readLength*100/countLength).toInt())
        }
    
        override fun onError(t: Throwable) {
            println(t.message)
        }
    })
    
    • DownloadInfo

      data class DownloadInfo(
            /**
             * 域名
             */
            val baseUrl:String,
            /**
             * 下载地址
             */
            val url:String,
            /**
             * 连接超时时间(单位:秒)
             */
            val connectTimeout:Long = 5
      ){
              override fun toString(): String {
                      return "$baseUrl$url"
              }
      }
      
    • DownloadCallback

      interface DownloadCallback {
          /**
           * 开始下载
           */
          fun onStart()
      
          /**
           * 下载成功
           * @param path 本地保存的地址
           */
          fun onSuccess(path:String)
      
          /**
           * 下载完成
           */
          fun onComplete()
      
          /**
           * 下载进度
           * @param readLength 读取的进度
           * @param countLength 总进度
           */
          fun onProgress(readLength:Long,countLength:Long)
      
          /**
           * 下载错误
           * @param t 错误信息
           */
          fun onError(t:Throwable)
      }
      

第三方库

SmartRefreshLayout-刷新控件

Immersionbar-状态栏

Koin

RxJava3

RxAndroid

OkHttp

Retrofit

Gson

AndroidUtilKTX-工具类

LoadingView-Loading动画