Skip to content
mvp and mvvm with lifecycle
Kotlin
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app 删除无用文件 Jan 19, 2020
gradle/wrapper
library
.gitignore
.project
README.md
build.gradle
common-version.gradle
gradle.properties first commit Dec 10, 2019
gradlew first commit Dec 10, 2019
gradlew.bat first commit Dec 10, 2019
publish.gradle
settings.gradle 优化细节 Jan 19, 2020
test.txt

README.md

引入方法

implementation 'com.susion:life-clean:1.0.1'

介绍

LifeClean提供了一个稳定、简单的方式来组织你的Android代码,主要有以下特点:

  1. PresenterViewModel提供生命周期组件LifeOwner
  2. 使View具有感知Activity生命周期的能力
  3. 新的MVP书写方式, ViewPresenter完全解耦
  4. 摒弃Fragment, 基于``View`构建应用UI
  5. 提供RxDisposeManager,可以非常方便的释放Disposable,避免内存泄漏
  6. 规范页面的更新状态、统一RcyclerView的用法

LifeClean的目标是构建一份简单易维护的业务代码

PresenterViewModel提供LifeOwner

通过LifeClean来对PresenterViewModel进行创建:

val presenter: LifePresenter =  LifeClean.createPresenter<GithubPresenter, SimpleRvPageProtocol>(context, this) //context 为 activity, this为View所遵守的页面协议

经过LifeClean实例化的Presenter可以很方便的拿到LifeOwner:

class GithubPresenter(val view: SimpleRvPageProtocol) : LifePresenter() {

    private fun loadSearchResult(query: String, isLoadMore: Boolean = false) {
        apiService
        .searchRepos(query + IN_QUALIFIER, requestPage, PAGE_SIZE)
        .subscribe({
            ...
        }, {
            ...
        }).disposeOnDestroy(getLifeOwner())
    }

}

即上面通过getLifeOwner()方法可以很方便的获取LifeOwner来对Disposable进行及时释放。

ViewModel的创建类似:

private val viewModel by lazy {
    LifeClean.createLifeViewModel<GithubViewModel>(context)  //context 为 activity
}

View提供感知Activity生命周期的能力

LifeClean中需要感知Activity生命周期的View首先需要实现LifePage:

interface LifePage : LifecycleObserver

然后通过LifeClean来构建View实例:

val lifePage = LifeClean.createPage<GitHubLifePage>(this)  //this 为 activity

这样创建的GitHubLifePage就可以感知Activity的生命周期事件:

class GitHubLifePage(activity: AppCompatActivity) : FrameLayout(context),LifePage {

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() {
        Toast.makeText(context, "接收到Activity的生命周期事件 onResume", Toast.LENGTH_SHORT).show()
    }
}

完全解耦ViewPresenter

LifeClean中定义的ViewPresenter的交互接口如下:

interface Presenter {
    /**
     * view 发送Action, Presenter可以选择处理这个时间
     * */
    fun dispatch(action: Action)

    /**
     * view 希望获得的数据状态,这种数据一般是指一种作用范围很大的数据状态
     * */
    fun <T : State> getState(): T? {
        return null
    }
}

getStatus()默认为空实现, 因为在完全遵守数据驱动UI的思想中, 一个View很少需要知道更大作用域的数据状态。

即:

  1. View只能通过发送一个Action告诉Presenter要做的事情。
  2. View可以通过getStatus()来查询需要的数据状态。

LifePresenter继承自Presenter

LifeCleanViewPresenter的交互应基于约定(protocol),约定(protocol)中定义View能发出的Action、需要获取的StateView的刷新接口。比如:

interface SimpleRvPageProtocol {

    /**
     * 该页面发出的事件
     * */
    class LoadData(val searchWord: String, val isLoadMore: Boolean) : Action

    /**
     * 该页面需要的状态
     * */
    class PageState(val currentPageSize: Int) : State

    fun refreshDatas(datas: List<Any>, isLoadMore: Boolean = false, extra: Any = Any())

    fun refreshPageStatus(status: String, extra: Any = Any())

}

上面是一个很通用的页面协议,已经被集成到LifeClean中。

结合上面谈到的两点, 使用LifeClean定义出一个页面(View)的写法如下:

class GitRepoMvpPage(context: AppCompatActivity) : SimpleRvPageProtocol, FrameLayout(context) {
    
    private val presenter: LifePresenter by lazy {
        LifeClean.createPresenter<GithubPresenter, SimpleRvPageProtocol>(context, this)
    }

    init {
        .....
        presenter.dispatch(SimpleRvPageProtocol.LoadData("Android", false))
    }

    ...
}

即在LifeClean中,View基本只使用Presenter.dispatch(action)方法与Presenter交互, 对于View发出的事件,Presenter可以选择处理,也可以选择不处理:

class GithubPresenter(val view: SimpleRvPageProtocol) : LifePresenter() {
   override fun dispatch(action: Action) {
        when (action) {
            is SimpleRvPageProtocol.LoadData -> {
                loadSearchResult(action.searchWord, action.isLoadMore)
            }
        }
    }
}

在这里Presenter是耦合于约定(Protocol)的。

RxDisposeManager

现在使用RxJava来开发app基本已成为主流,为了解决RxJava的内存泄漏问题, LifeClean提供了RxDisposeManager,具体用法如下:

  apiService
        .searchRepos(query + IN_QUALIFIER, requestPage, PAGE_SIZE)
        .subscribe({
            ...
        }, {
            ...
        }).disposeOnDestroy(getLifeOwner())

即调用disposeOnDestroy(getLifeOwner()),disposeOnDestroy()接收的参数是LifeOwner。它会观察LifeOwner的生命周期事件并及时释放掉Disposable

规范页面状态

LifeClean简单的定义了下面这个页面状态类:

object PageStatus {

    val START_LOAD_MORE = "start_load_more"
    val END_LOAD_MORE = "end_load_more"
    val START_LOAD_PAGE_DATA = "start_load_page_data"
    val END_LOAD_PAGE_DATA = "end_load_page_data"
    val NO_MORE_DATA = "no_more_data"
    val EMPTY_DATA = "empty_data"
    val NET_ERROR = "net_error"
    val TOAST = "show_toast"
    val PRIVACY_DATA = "privacy_data"
    val CONTENT_DELETE = "content_delete"
    val ERROR = "error"
    val UNDEFINE = "undefine"
    ...
}

即你可以使用类似下面的方法来使你的app统一页面状态的更新:

    apiService.searchRepos(query + IN_QUALIFIER, requestPage, PAGE_SIZE)
    .doOnSubscribe {
        view.refreshPageStatus(if (isLoadMore) PageStatus.START_LOAD_MORE else PageStatus.START_LOAD_PAGE_DATA)
    }.doOnTerminate {
        view.refreshPageStatus(if (isLoadMore) PageStatus.END_LOAD_MORE else PageStatus.END_LOAD_PAGE_DATA)
    }.subscribe({ dataList->

        if(dataList.isEmpty){
             view.refreshPageStatus(PageStatus.EMPTY_DATA)
        }

    },{error->
        view.refreshPageStatus(PageStatus.ERROR)
    })

统一RecyclerView的用法

LifeClean中所有RecyclerViewItemView都必须继承自View并实现下面这个约定 :

interface AdapterItemView<T> {
    fun bindData(data: T, position: Int)
}

ItemView只需要简单的拿到数据渲染UI即可。

这个约定是通过Adapter来实现的, 你需要使用LifeClean中定义好的Adapter,比如SimpleRvAdapter, 举个例子 :

定义ItemView

class GitRepoView(context: Context) : AdapterItemView<Repo>, ConstraintLayout(context) {

    init {
        LayoutInflater.from(context).inflate(R.layout.repo_view_item, this)
        ......
    }

    override fun bindData(repo: Repo, position: Int) {
        repo_name.text = repo.fullName
        ...
    }
}

对于ItemView做这种限制的原因是因为: 不希望把ItemView渲染的代码写在页面中,比如Activity

使用SimpleRvAdapter

class GitRepoMvpPage(activity: AppCompatActivity) : SimpleRvPageProtocol, FrameLayout(context) {

    private val adapter = SimpleRvAdapter<Any>(activity)).apply {
        registerMapping(String::class.java, SimpleStringView::class.java)
        registerMapping(Repo::class.java, GitRepoView::class.java)
    }

}

SimpleRvAdapter提供了快速注册ItemView的能力。

You can’t perform that action at this time.