当前位置: 代码迷 >> 综合 >> 【安卓结构整理笔记】MVC,MVP,MVVM
  详细解决方案

【安卓结构整理笔记】MVC,MVP,MVVM

热度:12   发布时间:2023-12-15 11:14:29.0

一.命名规约

该文档为视频学习的个人笔记,图片源自b站up:程序员拉大锯https://www.bilibili.com/video/BV1Dk4y1C7mm?p=7

  1. 控件名:Login_btn
  2. 函数名:toLogin()
  3. 类名: UserModel
  4. 接口:OnDoLoginStateChange()
  5. 静态常量: const val STATE_LOGIN_LOADING=0 ,置于compain object中
  6. 普通宽700,模块图600x300

二.MVC表征

1.M-VC架构图

在这里插入图片描述

2.MVC特征

  1. Activity中controler与view联系紧密
  2. Model类提供方法doLogin,其方法调用自身接口方法,形成Model架构,在AC的Controler中惰性加载一个对象,由此对象调用方法并实现接口

3.补充两种接口回调

  1. lambda表达式,参数第二个lambda决定了有block回调,Unit决定了调用时不必写参数,调用出可以直接打开{}获取其invoke的返回值
    1)写法
    在这里插入图片描述
    2)调用方法
    加粗样式
  2. 若要让调用函数的对象自由实现具体行为,则应写一个callback,实现为某个待实现接口类型,让具体调用对象实现接口,实现框架返回的对应某个接口。将doLogin方法。

4.M-VC测试代码

在这里插入图片描述

  1. MainActivity
class MainActivity : AppCompatActivity(), UserModel.OnDoLoginStateChange {
    private val usermodel by lazy {
    UserModel()}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)initlistener()}fun initlistener(){
    login_btn.setOnClickListener{
    toLogin()}}private fun toLogin() {
    val account:String=input_name.text.toString()val password:String=input_password.text.toString()if (TextUtils.isEmpty(account)){
    println("输入账号为空,请重新输入")return}if (TextUtils.isEmpty(password)){
    println("输入密码为空,请重新输入")return}usermodel.checkUserState(account){
    when(it){
    0->{
    //不可用}1->{
    usermodel.doLogin(this,account,password)//异步操作 禁止按钮可以点击login_btn.isEnabled=false}}}}override fun onLoading() {
    login_TipsText.text="登陆中...."}override fun onLoadSuccess() {
    login_TipsText.text="登陆成功...."}override fun onLoadFailed() {
    login_TipsText.text="登陆失败...."}
}
  1. Model类,提供登录与检查架构调用的接口,有C来实现接口具体操作

class UserModel {
    private val api by lazy {
    API()}private val random=Random()fun doLogin(callback: OnDoLoginStateChange, account: String, password: String) {
    callback.onLoading()//调用开始登录的API//此结果为耗时操作 返回0,1val randomValue:Int=random.nextInt(2)if (randomValue==0){
    callback.onLoadSuccess()}else{
    callback.onLoadFailed()}}fun checkUserState(account: String,block:(Int)->Unit){
    //一种写法//0表示已注册 1表示未注册block.invoke(random.nextInt(2))}interface OnDoLoginStateChange{
    fun onLoading()fun onLoadSuccess()fun onLoadFailed()}
}

5. 小结

在这里插入图片描述

二.MVP表征

1.M-V-P架构图

2.MVP特征

  1. 直接抽取Controler类->Presenter,Presenter负责Model与View的联通(中转站)。
  2. Model类提供方法doLogin,用于获取具体数据
  3. Presenter中惰性加载一个model对象,由此对象调用方法并实现接口,提供 checkUserNameState方法供View层调用根据框架接口,返回对应接口让主类实现。

3.补充在这里插入图片描述

4. 代码

在这里插入图片描述

  1. MainActivity->`LoginActivity
    1)首先将MainActivity中的V-C分离,抽取LoginPresenter,并且初始化model(主界面不和model)增设检查方法checkUserNameState(),参数为传入账号与返回callback回调,内部具体控制model实现。回调结果为成功与失败。其中转了model层的check方法,拿到数据,据相应数据回调接口各种。
    2)把原本直接调用model层的登录方法,移至Presenter中,回调不变,
    3)这步重要的是P层onLogin方法中,不仅有回调框架 并且调用了model层的方法,model可以直接返回一个数据给P层,所以中转了model层的doLogin方法,拿到数据,据相应数据回调接口各种。

class LoginActivity : AppCompatActivity(),LoginPresenter.OnDoLoginStateChange,LoginPresenter.OnCheckUserStateResultCallback {
    //改为声明P层private val loginPresenter by lazy {
    LoginPresenter()}private var isUserNameCanBeUser=falseoverride fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)initlistener()}fun initlistener(){
    login_btn.setOnClickListener{
    toLogin()}//1 增设监听内容变化 文字改变时检查注册回调input_name.addTextChangedListener(object :TextWatcher{
    override fun afterTextChanged(s: Editable?) {
    }override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    // TODO("Not yet implemented")}//1.1当变化时 调用P层的验证用户名 并且实现回调接口 p层据情况分流走不同的两个接口框架override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    loginPresenter.checkUserNameState(s.toString(),this@LoginActivity)}})}//2 进行登录时调用p层方法 并且回调 和1一样private fun toLogin() {
    val account:String=input_name.text.toString()val password:String=input_password.text.toString()if (!isUserNameCanBeUser){
    //提示用户当前用户名已被注册}loginPresenter.doLogin(account,password,this)//第三个参数传this让当前类实现}override fun onAccountFormatError() {
    login_TipsText.text="账号不可以为空"}override fun onPasswordEmpty() {
    login_TipsText.text="密码不可以为空"}//外部实现override fun onNotExist() {
    login_TipsText.text="该用户可以使用"isUserNameCanBeUser=true}override fun onExist() {
    login_TipsText.text="该用户名已注册"isUserNameCanBeUser=false}override fun onLoading() {
    login_TipsText.text="登陆中...."}override fun onLoadSuccess() {
    login_TipsText.text="登陆成功...."}override fun onLoadFailed() {
    login_TipsText.text="登陆失败...."}}
  1. LoginPresenter
class LoginPresenter {
    //v不和model打交道private val usermodel by lazy {
    UserModel()}fun checkUserNameState(account:String,callback:OnCheckUserStateResultCallback){
    usermodel.checkUserState(account){
    println(it)when(it){
    0->{
    callback.onExist()}1->{
    callback.onNotExist()}}}}interface  OnCheckUserStateResultCallback{
    fun onNotExist()fun onExist(}fun doLogin(username:String,password:String,callback: OnDoLoginStateChange){
    if (TextUtils.isEmpty(username)){
    println("输入账号为空,请重新输入")callback.onAccountFormatError()return}if (TextUtils.isEmpty(password)){
    println("输入密码为空,请重新输入")callback.onPasswordEmpty()return}usermodel.doLogin(username,password){
    when(it){
    STATE_LOGIN_LOADING->{
    callback.onLoading()}STATE_LOGIN_SUCCESS->{
    callback.onLoadSuccess()}STATE_LOGIN_FAILED->{
    callback.onLoadFailed()}}}}interface OnDoLoginStateChange{
    fun onAccountFormatError()fun onPasswordEmpty()fun onLoading()fun onLoadSuccess()fun onLoadFailed()}
}
  1. UserModel
class UserModel {
    companion object{
    const val STATE_LOGIN_LOADING=0const val STATE_LOGIN_SUCCESS=1const val STATE_LOGIN_FAILED=2}private val api by lazy {
    API()}private val random=Random()fun doLogin( account: String, password: String,block: (Int) -> Unit) {
    block.invoke(STATE_LOGIN_LOADING)//调用开始登录的API//此结果为耗时操作 返回0,1val randomValue:Int=random.nextInt(2)if (randomValue==0){
    block.invoke(STATE_LOGIN_SUCCESS)}else{
    block.invoke(STATE_LOGIN_FAILED)}}fun checkUserState(account: String,block:(Int)->Unit){
    //一种写法//0表示已注册 1表示未注册block.invoke(random.nextInt(2))}}

5. 小结

缺点例如:接口的每个方法都要实现出来,但实践中只需调用其中几个方法即可,造成冗余。
在这里插入图片描述

2.5 MVP音乐播放样例 【UI驱动开发与单例模式】

在这里插入图片描述

  1. 主Activity,PlayerActivity,实现音乐播放/暂停等接口方法,与P层以结构接口方式交互,实现对应接口的方法,惰性加载presenter类

class PlayerActivity:AppCompatActivity(), IPlayCallback {
    private val playerplaysenter by lazy {
    PlayPresenter()}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_player)playerplaysenter.registerCallback(this)//传递了this并且声明主类实现回调initlistener()}private fun initlistener() {
    play_button.setOnClickListener{
    //调用p层播放暂停方法playerplaysenter.doPlayOrPause()}play_next.setOnClickListener{
    playerplaysenter.playNext()}play_pre.setOnClickListener {
    playerplaysenter.playPre()}}override fun onDestroy() {
    super.onDestroy()if (playerplaysenter!=null){
    playerplaysenter.unRegisterCallback(this)}}override fun OnTitleChange(title: String) {
    play_title?.text=title}override fun OnProgressChange(current: Int) {
    }override fun OnPlaying() {
    //显示暂停play_button.text="暂停"}override fun OnPlayerPause() {
    play_button.text="播放"}override fun OnCoverChange(cover: String) {
    println("封面改变了"+cover)}
}
  1. 接口类:IPlayCallback,定义5种状态回调
interface IPlayCallback {
    fun OnTitleChange(title:String)fun OnProgressChange(current:Int)fun OnPlaying()fun OnPlayerPause()fun OnCoverChange(cover:String)
}
  1. P层控制类:PlayPresenter,由于可能会有多个AC需调用这个接口,所以使用list管理,对应Activity销毁时将其P写个register方法进行add管理,销毁时remove进行资源管理。
class PlayPresenter {
    //private val callbackList= arrayListOf<IPlayCallback>()enum class PlayState{
    NONE,PLAYING,PAUSE,LOADING}private var currentplaystate=PlayState.NONE//可能会有多个地方调用 使用集合管理接口回调fun registerCallback(callback: IPlayCallback) {
    //注意这里直接就是回调接口if (!callbackList.contains(callback)){
    //如果不存在callbackList.add(callback)}}fun unRegisterCallback(callback: IPlayCallback){
    callbackList.remove(callback)}//根据状态播放音乐 播放或是暂停fun doPlayOrPause() {
    dispatchTitleChange("当前播放的歌曲标题....")dispatchCoverChange("当前播放的歌曲封面...")if (currentplaystate!=PlayState.NONE){
    //开始播放音乐dispatchPlayingState()currentplaystate=PlayState.PLAYING}else{
    //暂停dispatchPauseState()currentplaystate=PlayState.PAUSE}}private fun dispatchPauseState() {
    callbackList.forEach(){
    it.OnPlayerPause()}}private fun dispatchPlayingState() {
    callbackList.forEach(){
    it.OnPlaying()}}fun playNext() {
    /** 1.拿到下一首 变更UI,包括标题和封面* 2.设置播放器* 3.等待播放的回调通知*/currentplaystate=PlayState.PLAYINGdispatchTitleChange("切换到下一首,标题变化了")dispatchCoverChange("切换到下一首,封面变化了")}private fun dispatchCoverChange(cover:String) {
    callbackList.forEach{
    it.OnCoverChange(cover)}}private fun dispatchTitleChange(title:String) {
    callbackList.forEach{
    it.OnTitleChange(title)}}fun playPre() {
    currentplaystate=PlayState.PLAYINGdispatchTitleChange("切换到上一首,标题变化了")dispatchCoverChange("切换到上一首,封面变化了")}}
  1. 此Activity回调实现5个接口方法,下面是xml
    在这里插入图片描述

  2. 设置悬浮按钮Activity【功能只涵盖 播放/停止】,新建AC:FlowPlayerControlActivity,则会有上下一曲这两个接口不用实现的冗余。


class FlowPlayerControlActivity:AppCompatActivity(), IPlayCallback {
    //单例private val playerPresenter by lazy {
    PlayPresenter()}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_flow_player)playerPresenter.registerCallback(this)initlistener()}private fun initlistener() {
    play_orpausebtn.setOnClickListener{
    playerPresenter.doPlayOrPause()}}override fun OnTitleChange(title: String) {
    }override fun OnProgressChange(current: Int) {
    }override fun OnPlaying() {
    play_orpausebtn.text="暂停"}override fun OnPlayerPause() {
    play_orpausebtn.text="播放"}override fun OnCoverChange(cover: String) {
    }override fun onDestroy() {
    super.onDestroy()playerPresenter.unRegisterCallback(this)}
}
  1. Presenter优化为单例模式
    1)P层私有结构化方法,当有多个Activity需要Presenter控制时,调用静态instance即可。
    在这里插入图片描述

2.6 例一:音乐播放MVP接口改良【数据驱动模式-观察者模式

1.扉

  1. 之前是使用Presneter注册接口回调更新Ui层,现在改为监听Presenter数据解决接口方法得一起调用的问题。核心是监听具体是数据,样例中是 播放状态当前播放的音乐。方法是采用数据容器,通过lambda的方式进行set接口回调。
    在这里插入图片描述
    在这里插入图片描述

2.代码

  1. 新建Music bean类

class Music(val name:String,val cover:String,val url:String) {
    
}

1)PlayPresenter中,先设置变量当前播放音乐,在设置播放状态变量,接下来根据播放情况,在Play时进行变换,调用Model层方法
在这里插入图片描述
2. 建立PlayerModel类,获取具体数据,可以先写固定数据

class PlayerModel {
    fun getMusicById(id: String): Music{
    return Music("歌曲名$id","file:///F:/daily/%E6%89%89/WEB/html%E2%80%A2%E6%94%B9/img/10.jpg","https://mp.csdn.net/console/article")}
}
  1. 建立Player类,用于模拟设置播放音乐,PlayPresent中设置play(currentmusic)
class MusicPlayer {
    fun play(music:Music?){
    }}
  1. 监听数据,提供一个容器,新建类DataListenContainer,目的是让数据可以被监听`当数据变化时,就通知更新
class DataListenContainer<T> {
    private val blocks= arrayListOf<(T?)->Unit>()//有可能多个监听 所以将其保存起来var value: T ?=null//当数据变化的时候,就通知更新set(value) {
    blocks.forEach{
    it.invoke(value)}}fun addlistener(block:(T?)->Unit){
    if (!blocks.contains(block)){
    blocks.add(block)}}
}

1)修改Presenter声明
在这里插入图片描述
2)UI中设置监听
在这里插入图片描述3)更改相应的上一曲下一曲,并且将之前接口注册什么的给去掉,也不需要实现接口回调了

  1. PlayPresenter
class PlayPresenter private  constructor(){
    //private val playermodel by lazy {
    PlayerModel()}private val player by lazy {
    MusicPlayer()}//改造使这两个数据皆可被监听 去掉private使外部可监听数据变化var currentmusic=DataListenContainer<Music>()var currentplaystate=DataListenContainer<PlayState>()companion object{
    val instance by lazy {
    PlayPresenter()}}//private val callbackList= arrayListOf<IPlayCallback>()enum class PlayState{
    NONE,PLAYING,PAUSE,LOADING}/* //可能会有多个地方调用 使用集合管理接口回调fun registerCallback(callback: IPlayCallback) {if (!callbackList.contains(callback)){//如果不存在callbackList.add(callback)}}fun unRegisterCallback(callback: IPlayCallback){callbackList.remove(callback)}*///根据状态播放音乐 播放或是暂停fun doPlayOrPause() {
    if (currentmusic.value==null){
    //获取一首歌currentmusic.value=playermodel.getMusicById("夏鱼")}player.play(currentmusic.value)
/*dispatchTitleChange("当前播放的歌曲标题....")dispatchCoverChange("当前播放的歌曲封面...")*/if (currentplaystate.value!=PlayState.NONE){
    //开始播放音乐// dispatchPlayingState()currentplaystate.value=PlayState.PLAYING}else{
    //暂停//dispatchPauseState()currentplaystate.value=PlayState.PAUSE}}/*private fun dispatchPauseState() {callbackList.forEach(){it.OnPlayerPause()}}private fun dispatchPlayingState() {callbackList.forEach(){it.OnPlaying()}}*/fun playNext() {
    /** 1.拿到下一首 变更UI,包括标题和封面* 2.设置播放器* 3.等待播放的回调通知*/currentmusic.value=playermodel.getMusicById("下一首:夏鱼")/*dispatchTitleChange("切换到下一首,标题变化了")dispatchCoverChange("切换到下一首,封面变化了")*/currentplaystate.value=PlayState.PLAYING}
/*private fun dispatchCoverChange(cover:String) {callbackList.forEach{it.OnCoverChange(cover)}}private fun dispatchTitleChange(title:String) {callbackList.forEach{it.OnTitleChange(title)}}*/fun playPre() {
    currentmusic.value=playermodel.getMusicById("上一首:春鸭")currentplaystate.value=PlayState.PLAYING/* dispatchTitleChange("切换到上一首,标题变化了")dispatchCoverChange("切换到上一首,封面变化了")*/}}
  1. PlayerActivity
class PlayerActivity:AppCompatActivity(){
    private val playerplaysenter by lazy {
    PlayPresenter.instance}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_player)/* playerplaysenter.registerCallback(this)*/initlistener()//更改后不再关心里面的状态变化 只关心数据变化initDataListener()}
//对数据进行监听 传一个容器类DLCprivate fun initDataListener() {
    playerplaysenter.currentmusic.addlistener {
    //更新UI 音乐内容发生变化play_title.text=it?.nameprintln("222222222222")println("封面改变了。。。${
      it?.cover}")}playerplaysenter.currentplaystate.addlistener {
    when(it){
    PlayPresenter.PlayState.PAUSE->{
    play_button.text="播放"}PlayPresenter.PlayState.PLAYING->{
    play_button.text="暂停"}}}}private fun initlistener() {
    play_button.setOnClickListener{
    //调用p层播放暂停方法playerplaysenter.doPlayOrPause()}play_next.setOnClickListener{
    playerplaysenter.playNext()}play_pre.setOnClickListener {
    playerplaysenter.playPre()}}
/*override fun onDestroy() {super.onDestroy()if (playerplaysenter!=null){playerplaysenter.unRegisterCallback(this)}}override fun OnTitleChange(title: String) {play_title?.text=title}override fun OnProgressChange(current: Int) {}override fun OnPlaying() {//显示暂停play_button.text="暂停"}override fun OnPlayerPause() {play_button.text="播放"}override fun OnCoverChange(cover: String) {println("封面改变了"+cover)}*/
}

9.FlowPlayerControlActivity 这里实现了数据监听,所以回调时通过lambda返回值判断打印即可。


class FlowPlayerControlActivity:AppCompatActivity() {
    //单例private val playerpresenter by lazy {
    PlayPresenter.instance}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_flow_player)//playerPresenter.registerCallback(this)initlistener()initDataListener()}private fun initDataListener() {
    //监听数据变化playerpresenter.currentplaystate.addlistener {
    /* if(it===PlayPresenter.PlayState.PLAYING){play_orpausebtn.text="暂停"}else{play_orpausebtn.text="播放"}*/play_orpausebtn.text=if (it===PlayPresenter.PlayState.PLAYING){
    "暂停"}else{
    "播放"}}}private fun initlistener() {
    play_orpausebtn.setOnClickListener{
    playerpresenter.doPlayOrPause()}}}

2.7例二:音乐列表MVP

  1. MusicActivity
    1)数据驱动,首先监听数据【列表和获取状态】
    2)这次加入了子线程查询回调时打印了线程名,发现不是Main线程,所以不能更新UI(会炸毛)
    在这里插入图片描述
class MusicActivity:AppCompatActivity() {
    private val musicpresenter by lazy{
    MusicPresenter()}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_music)initDataListener()initViewListener()}private fun initDataListener() {
    musicpresenter.musiclist.addlistener {
    println(Thread.currentThread().name)//数据变化println("加载状态---> ${
      it?.size}")}musicpresenter.loadState.addlistener {
    println("加载状态---> $it")}}private fun initViewListener() {
    get_musicbutton.setOnClickListener {
    musicpresenter.getMusic()}}
}
  1. MusicPresenter
    1)单例模式,使View层可获取列表和播放状态并监听,其主要功能是与model层交互获取两个数据的具体值

class MusicPresenter {
    enum class MusicLoadState{
    LOADING,EMPTY,SUCCESS,ERROR}private val musicmodel by lazy {
    MusicModel()}val musiclist=DataListenContainer<List<Music>>()val loadState=DataListenContainer<MusicLoadState>()private val page=1private val size=30fun getMusic(){
    loadState.value=MusicLoadState.LOADING//请求从Model层获取数据musicmodel.loadMusicByPage(page,size,object :MusicModel.OnMusicLoadResult{
    override fun onSuccess(result: List<Music>) {
    musiclist.value=resultloadState.value=if (result.isEmpty()){
    MusicLoadState.EMPTY}else{
    MusicLoadState.SUCCESS}}override fun onError(msg: String, code: Int) {
    loadState.value=MusicLoadState.ERRORprintln("error...$msg...$code")}//model层 写接口})}}
  1. MusicModel
    1)开启子线程 回调返回数据。
    2)此处遍历方式,若改为…则包含30为0~30size为31,until不包含右边界。
class MusicModel {
    fun loadMusicByPage(page:Int,size:Int,callback:OnMusicLoadResult){
    val result = arrayListOf<Music>()//开启线程Thread{
    for (i in (0 until size)) {
    //until 0-30 比.. 少1result.add(Music("音乐的名称 $i","cover 封面 $i","utl == > $i"))}//数据callback.onSuccess(result)}.start()}interface  OnMusicLoadResult{
    fun onSuccess(result: List<Music>)fun onError(msg:String,code:Int)}
}

2.75 封装进入主线程

在这里插入图片描述

  1. MusicActivity中加入这个,以驱动界面UI显示音乐数量
    在这里插入图片描述
  2. 增设App
    1)用于调用主线程
    2)在清单文件中注册
class App:Application() {
    companion object{
    val handler= Handler()//主线程静态handler}override fun onCreate() {
    super.onCreate()}
}

在这里插入图片描述
3. 在统一回调中判断线程,若非主线程,则调用App,handler主线程post处理操作

class DataListenContainer<T> {
    private val blocks= arrayListOf<(T?)->Unit>()//有可能多个监听 所以将其保存起来var value: T ?=null//当数据变化的时候,就通知更新set(value) {
    //判断当前线程是不是主线程//如果是则直接调用,否则切换到主线程if(Looper.getMainLooper().thread== Thread.currentThread()){
    blocks.forEach{
    it.invoke(value)//相当于lambd表达式回调的更新 方法}}else{
    App.handler.post {
    blocks.forEach{
    it.invoke(value)//相当于lambd表达式回调的更新 方法}}}}fun addlistener(block:(T?)->Unit){
    if (!blocks.contains(block)){
    blocks.add(block)}}
}

2.8 活动的生命周期-》为避免资源浪费(UI不活跃即可不通知更新,最终是为了解决MVP的UI是否销毁缺陷)Viw层通知Presenter层感知生命周期变化

在这里插入图片描述
在这里插入图片描述
1)在MusicActivity中加入presenter方法的同步更新
在这里插入图片描述
2)presenter只是一个普通类,写一个接口,OnLifeCycle,让其继承即可同步了,问题是 总不可能每个调用presenter单例的都要进行生命周期以一个个启动的方式对接-》抽取BaseActivity(和FarawayPlayer连接上了)。

interface OnLifecycle {
    fun onCreate()fun onStart()fun onResume()fun onPause()fun onStop()fun onDestory()
}

3)BaseActivity


open class BaseActivity:AppCompatActivity() {
    
//可能与多个lifecycle对象监听 所以使用列表存起来private val lifecycleListener= arrayListOf<OnLifecycle>()fun addLifeListener(listener: OnLifecycle){
    if (!lifecycleListener.contains(listener)){
    lifecycleListener.add(listener)}}fun removeLifeListener(listener: OnLifecycle){
    lifecycleListener.remove(listener)}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)lifecycleListener.forEach{
    //原本通过某一具体对象调用变为遍历找到该对象通过it绑定it.onCreate()}//musicpresenter.onCreate()}override fun onStart() {
    super.onStart()//musicpresenter.onStart()lifecycleListener.forEach{
    it.onStart()}}override fun onResume() {
    super.onResume()lifecycleListener.forEach{
    it.onResume()}}override fun onPause() {
    super.onPause()lifecycleListener.forEach{
    it.onPause()}}override fun onDestroy() {
    super.onDestroy()lifecycleListener.forEach{
    it.onDestory()}}override fun onStop() {
    super.onStop()lifecycleListener.forEach{
    it.onStop()}}}

在这里插入图片描述
4)更改MusicActivity,去除接口,使用BaseActivity的addlifelistener即可,不需写重复代码。


class MusicActivity:BaseActivity() {
    private val musicpresenter by lazy{
    MusicPresenter()}init {
    addLifeListener(musicpresenter)}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_music)initDataListener()initViewListener()//使presenter知道生命周期的变化/*musicpresenter.onCreate()*/}private fun initDataListener() {
    musicpresenter.musiclist.addlistener {
    println(Thread.currentThread().name)//数据变化println("加载状态---> ${
      it?.size}")music_count.text="加载到了--->${
      it?.size}条数据"}musicpresenter.loadState.addlistener {
    println("加载状态---> $it")}}private fun initViewListener() {
    get_musicbutton.setOnClickListener {
    musicpresenter.getMusic()}}
}/* private fun MusicPresenter.onCreate() { } */

5)PlayerActivity同理,先修改present实现接口,再通过继承BaseActivity来addlglistener

lass PlayPresenter private  constructor():OnLifecycle{
    //private val playermodel by lazy {
    PlayerModel()}private val player by lazy {
    MusicPlayer()}//改造使这两个数据皆可被监听 去掉private使外部可监听数据变化var currentmusic=DataListenContainer<Music>()var currentplaystate=DataListenContainer<PlayState>()companion object{
    val instance by lazy {
    PlayPresenter()}}//private val callbackList= arrayListOf<IPlayCallback>()enum class PlayState{
    NONE,PLAYING,PAUSE,LOADING}/* //可能会有多个地方调用 使用集合管理接口回调fun registerCallback(callback: IPlayCallback) {if (!callbackList.contains(callback)){//如果不存在callbackList.add(callback)}}fun unRegisterCallback(callback: IPlayCallback){callbackList.remove(callback)}*///根据状态播放音乐 播放或是暂停fun doPlayOrPause() {
    if (currentmusic.value==null){
    //获取一首歌currentmusic.value=playermodel.getMusicById("夏鱼")}player.play(currentmusic.value)
/*dispatchTitleChange("当前播放的歌曲标题....")dispatchCoverChange("当前播放的歌曲封面...")*/if (currentplaystate.value!=PlayState.NONE){
    //开始播放音乐// dispatchPlayingState()currentplaystate.value=PlayState.PLAYING}else{
    //暂停//dispatchPauseState()currentplaystate.value=PlayState.PAUSE}}/*private fun dispatchPauseState() {callbackList.forEach(){it.OnPlayerPause()}}private fun dispatchPlayingState() {callbackList.forEach(){it.OnPlaying()}}*/fun playNext() {
    /** 1.拿到下一首 变更UI,包括标题和封面* 2.设置播放器* 3.等待播放的回调通知*/currentmusic.value=playermodel.getMusicById("下一首:夏鱼")/*dispatchTitleChange("切换到下一首,标题变化了")dispatchCoverChange("切换到下一首,封面变化了")*/currentplaystate.value=PlayState.PLAYING}
/*private fun dispatchCoverChange(cover:String) {callbackList.forEach{it.OnCoverChange(cover)}}private fun dispatchTitleChange(title:String) {callbackList.forEach{it.OnTitleChange(title)}}*/fun playPre() {
    currentmusic.value=playermodel.getMusicById("上一首:春鸭")currentplaystate.value=PlayState.PLAYING/* dispatchTitleChange("切换到上一首,标题变化了")dispatchCoverChange("切换到上一首,封面变化了")*/}override fun onCreate() {
    }override fun onStart() {
    //监听网络变化println("监听网络变化")}override fun onResume() {
    }override fun onPause() {
    }override fun onStop() {
    println("停止监听网络变化")}override fun onDestory() {
    }}
class PlayerActivity:BaseActivity(){
    private val playerplaysenter by lazy {
    PlayPresenter.instance}private val musicPresenter by lazy {
    MusicPresenter()}init {
    addLifeListener(musicPresenter)addLifeListener(playerplaysenter)}override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_player)/* playerplaysenter.registerCallback(this)*/initlistener()//更改后不再关心里面的状态变化 只关心数据变化initDataListener()}
//对数据进行监听 传一个容器类DLCprivate fun initDataListener() {
    playerplaysenter.currentmusic.addlistener {
    //更新UI 音乐内容发生变化play_title.text=it?.nameprintln("222222222222")println("封面改变了。。。${
      it?.cover}")}playerplaysenter.currentplaystate.addlistener {
    when(it){
    PlayPresenter.PlayState.PAUSE->{
    play_button.text="播放"}PlayPresenter.PlayState.PLAYING->{
    play_button.text="暂停"}}}}private fun initlistener() {
    play_button.setOnClickListener{
    //调用p层播放暂停方法playerplaysenter.doPlayOrPause()}play_next.setOnClickListener{
    playerplaysenter.playNext()}play_pre.setOnClickListener {
    playerplaysenter.playPre()}}

在这里插入图片描述
6)此时若有其他Activity则要重新再写一遍lifecycle相关代码,所以抽取LifecycleProvider

class LifecycleProvider {
    private var currentlifestate:LifeState?=nullprivate val lifecycleListener= arrayListOf<OnLifecycle>()fun addLifeListener(listener: OnLifecycle){
    if (!lifecycleListener.contains(listener)){
    lifecycleListener.add(listener)}}fun removeLifeListener(listener: OnLifecycle){
    lifecycleListener.remove(listener)}fun makeLifeState(state: LifeState){
    currentlifestate=statewhen(state){
    LifeState.CREATE->{
    diapatchCreateState()}LifeState.DESTROY->{
    diapatchCreateDestory()}LifeState.PAUSE->{
    diapatchCreatePause()}LifeState.START->{
    diapatchCreateStart()}LifeState.STOP->{
    diapatchCreateStop()}LifeState.RESUME->{
    diapatchCreateResume()}}}private fun diapatchCreateResume() {
    lifecycleListener.forEach{
    it.onResume()}}private fun diapatchCreateStop() {
    lifecycleListener.forEach{
    it.onStop()}}private fun diapatchCreateStart() {
    lifecycleListener.forEach{
    it.onStart()}}private fun diapatchCreatePause() {
    lifecycleListener.forEach{
    it.onPause()}}private fun diapatchCreateDestory() {
    lifecycleListener.forEach{
    it.onDestory()}lifecycleListener.clear()}private fun diapatchCreateState() {
    lifecycleListener.forEach{
    it.onCreate()}}}

在这里插入图片描述
BaseActivity对应调用
在这里插入图片描述
但此时有个问题,若是我要用Presenter内部类实现,而外部包裹类无需实现岂不是外部类不用实现而内部类实现OnLifecycle接口即可。
在这里插入图片描述
在这里插入图片描述
修改BaseActivity将注册动作由Presenter自己去完成。将下面init注释掉,但有个问题,get不到,所以写个getprovider方法
7)OnLifecycleOwner

interface OnLifecycleOwner {
    fun getLifecycleProvider():LifecycleProvider
}

再让BaseActivity实现,返回lifeprovider,子类想咋整咋整
在这里插入图片描述
这样回到presenter即可完成
在这里插入图片描述
同样来到PlayerActivity,传递this,然后不用init让presenter实现注册
在这里插入图片描述
同样LoginPresenter,继承接口再实现生命周期及方法
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.9 让数据容器具备感知View生命周期的能力(解决UI控件是否销毁的问题)

在这里插入图片描述
1)写一个接口,连个BaseView实现接口,回调方法实现返回对应lifeprovider
在这里插入图片描述
2)View层[ac和fg]继承两个Base
在这里插入图片描述
3)LifeProvider中通知状态变化
在这里插入图片描述
3.5)集合控制各个注册的接口
在这里插入图片描述
4)presenter要知道View的变化,定义出来时要构造出own接口,有了own即可getprovider并且添加,添加后即可通知。
在这里插入图片描述
5)View设置监听把owner给数据容器设置监听,即可拿到Provider
在这里插入图片描述
在这里插入图片描述
6)View和回调一一对应
在这里插入图片描述

7)销毁时也remove掉。
在这里插入图片描述

Jetpack

1. 修改Presenter

  1. Owner为官方接口在这里插入图片描述
  2. 观察者模式为官方监听
    在这里插入图片描述
  3. 实现的监听类改为官方类,好处就是
    在这里插入图片描述
    1)使用被动通知方式实现onStateChanged接口即可标记耦合监听
    在这里插入图片描述
    2)取代自己写的实现接口方法,其他override用不上的都去掉
    在这里插入图片描述
    3)简视图
    在这里插入图片描述
    4)方便了许多
    在这里插入图片描述
    5)有需要时也可主动获取当前状态
    在这里插入图片描述

2.修改数据容器DataListenContainer

  1. 改为官方接口在这里插入图片描述
  2. 修改储存对应类型
    在这里插入图片描述
  3. 此处的监听会报错
    在这里插入图片描述
  4. 改为官方监听
    1)改为官方监听
    在这里插入图片描述
    2)改为官方接口,以注解方法传递观察者实例
    在这里插入图片描述
    3)例子,以注解方法传递观察者实例
    在这里插入图片描述
  5. 修改抽取的更新代码,判断状态至少的start状态或resume才通知UI更新,否则UI不更新
    在这里插入图片描述
  6. HOME键是挂起,返回键是销毁,使用主动获取状态,注解灵活调用,Presenter使用被动更新
    在这里插入图片描述

3. Lifecycle小结

  1. Owner<–>view,提供Lifecycle接口
  2. Presenter和数据容器添加观察者方式监听
    在这里插入图片描述
  3. View层实现通知Lifecycle方式
    1)View源码实现了LifecycleOwner接口
    在这里插入图片描述
    2)要实现Lifecycle接口
    在这里插入图片描述
    3)以自己创建的方式
    在这里插入图片描述
    4)持有Lifecycle类的对象
    在这里插入图片描述
    5)当生命周期变化时,自动处理,到子类里就是前面的主动获取状态
    在这里插入图片描述
  4. Lifecycle以监听者方式通知Presenter生命周期变化
    1)监听者也会添加到一个集合里面去
    在这里插入图片描述
    2)状态变化时会调用这个方法,moveToState方法中这次状态和上次状态一样及不处理,否则就赋值给当前的state,再通过sync同步。最后会调用onStartChange方法,我们通过这个方法即可拿到对应数据
    在这里插入图片描述
  5. Lifecycle以注解方式通知DataListenContainer生命周期变化
    1)实现LifecycleObserver接口方式,其本身没有方法,但可以写注解,以回调的方式取到
    在这里插入图片描述
    2)ON_ANY方式进行监听,生命周期所有情况都会调用,太长了就不一一截图了,可以用来查看当前情况。
    在这里插入图片描述
    3)start周期owner与对应event
    在这里插入图片描述

4. LifeData(可观察的数据存储类)

  1. 和之前写的联系:做到若处于started或resumed往上的才会更新
    在这里插入图片描述
  2. 优势
    在这里插入图片描述
  3. 修改方式
    1)Presenter与Model联系中改用官方LifeData数据存储
    在这里插入图片描述
    2)加载成功后以post方式更新数据設置Value值,不用自己写切换主线程
    在这里插入图片描述
    3)修改监听器,监听presenter中的存储list
    在这里插入图片描述
    4)查看回调过程
    a. 先是Presenter层,onStateChanged方法察觉到了oncreate
    在这里插入图片描述
    b. 点击按钮后,livedata立刻就做出反应,回调给Activity,通知数据更新,再才是我们自己写的回调更新(可能是先监控livedata的原因)
    在这里插入图片描述

4.1 datalive另一种观察者方式后台继续更新

1)首先新建内部类继承Observer方法,复写onChanged方法处理回调,延迟加载对象
在这里插入图片描述
2)监听数据时,使用observeForever方法传递该对象,后台数据变更时也能实时反映。
在这里插入图片描述
3)Destory后必须将观察者移除,以免内存泄漏。
在这里插入图片描述

4.2 livedata共享数据

1. 样例

在这里插入图片描述

2. 图文举例,播放状态,主界面也需要该状态:先写一个类,继承LiveData再将其做成单例。

在这里插入图片描述

3. 实例代码

  1. 建立LivePlayerState单例模式并继承LiveData
class LivePlayerState private constructor():LiveData<PlayPresenter.PlayState>() {
    public override fun postValue(value: PlayPresenter.PlayState?) {
    super.postValue(value)}companion object{
    val instance by lazy {
    LivePlayerState()}}
}
  1. 来到PlayPresenter
    1)获取实例
    在这里插入图片描述
    2)状态变更处赋值
    在这里插入图片描述
    3)定义类方式进行对象监听
    在这里插入图片描述
  2. FlowPlayerControlActivity悬浮按钮中
    1)复制上面的代码,改为悬浮界面
  3. DataListenContainer保存对应状态时一定要赋值,即使下面用的是回调
    在这里插入图片描述
  4. 运行结果,通过第一个进入第二个悬浮按钮的活动,都OK
    在这里插入图片描述