一.命名规约
该文档为视频学习的个人笔记,图片源自b站up:程序员拉大锯https://www.bilibili.com/video/BV1Dk4y1C7mm?p=7
- 控件名:Login_btn
- 函数名:toLogin()
- 类名: UserModel
- 接口:OnDoLoginStateChange()
- 静态常量: const val STATE_LOGIN_LOADING=0 ,置于compain object中
- 普通宽700,模块图600x300
二.MVC表征
1.M-VC架构图
2.MVC特征
- Activity中controler与view联系紧密
- Model类提供方法doLogin,其方法调用自身接口方法,形成Model架构,在AC的Controler中惰性加载一个对象,由此对象调用方法并实现接口
3.补充两种接口回调
lambda表达式
,参数第二个lambda决定了有block回调,Unit决定了调用时不必写参数,调用出可以直接打开{}获取其invoke的返回值
。
1)写法
2)调用方法
- 若要让调用函数的对象
自由实现具体行为
,则应写一个callback
,实现为某个待实现接口类型,让具体调用对象实现接口,实现框架返回的对应某个接口
。将doLogin方法。
4.M-VC测试代码
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="登陆失败...."}
}
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特征
- 直接抽取Controler类->Presenter,Presenter负责Model与View的联通(中转站)。
- Model类提供方法doLogin,用于获取具体数据
- Presenter中惰性加载一个model对象,由此对象调用方法并实现接口,提供 checkUserNameState方法供View层调用根据框架接口,返回对应接口让主类实现。
3.补充
4. 代码
- 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="登陆失败...."}}
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()}
}
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驱动开发与单例模式】
- 主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)}
}
- 接口类:
IPlayCallback
,定义5种状态回调
interface IPlayCallback {
fun OnTitleChange(title:String)fun OnProgressChange(current:Int)fun OnPlaying()fun OnPlayerPause()fun OnCoverChange(cover:String)
}
- 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("切换到上一首,封面变化了")}}
-
此Activity回调实现5个接口方法,下面是xml
-
设置悬浮按钮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)}
}
Presenter
优化为单例模式
1)P层私有结构化方法,当有多个Activity需要Presenter控制时,调用静态instance即可。
2.6 例一:音乐播放MVP接口改良【数据驱动模式-观察者模式
】
1.扉
- 之前是使用
Presneter注册接口
回调更新Ui层,现在改为监听Presenter数据
,解决接口方法得一起调用的问题。核心是监听具体是数据,样例中是 播放状态 和 当前播放的音乐。方法是采用数据容器,通过lambda的方式进行set接口回调。
2.代码
- 新建
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")}
}
建立Player类
,用于模拟设置播放音乐,PlayPresent中设置play(currentmusic)
class MusicPlayer {
fun play(music:Music?){
}}
- 监听数据,提供一个容器,新建类
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)更改相应的上一曲下一曲,并且将之前接口注册什么的给去掉,也不需要实现接口回调了
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("切换到上一首,封面变化了")*/}}
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
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()}}
}
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层 写接口})}}
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 封装进入主线程
- MusicActivity中加入这个,以驱动界面UI显示音乐数量
- 增设
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
- Owner为官方接口
- 观察者模式为官方监听
- 实现的监听类改为官方类,好处就是
1)使用被动通知方式实现onStateChanged接口即可标记耦合监听
2)取代自己写的实现接口方法,其他override用不上的都去掉
3)简视图
4)方便了许多
5)有需要时也可主动获取当前状态
2.修改数据容器DataListenContainer
- 改为官方接口
- 修改储存对应类型
- 此处的监听会报错
- 改为官方监听
1)改为官方监听
2)改为官方接口,以注解方法传递观察者实例
3)例子,以注解方法传递观察者实例
- 修改抽取的更新代码,判断状态至少的start状态或resume才通知UI更新,否则UI不更新
- HOME键是挂起,返回键是销毁,使用
主动获取状态,注解灵活调用
,Presenter使用被动更新
3. Lifecycle小结
- Owner<–>view,提供Lifecycle接口
- Presenter和数据容器添加观察者方式监听
- View层实现通知Lifecycle方式
1)View源码实现了LifecycleOwner接口
2)要实现Lifecycle接口
3)以自己创建的方式
4)持有Lifecycle类的对象
5)当生命周期变化时,自动处理,到子类里就是前面的主动获取状态
- Lifecycle以监听者方式通知Presenter生命周期变化
1)监听者也会添加到一个集合里面去
2)状态变化时会调用这个方法,moveToState方法中这次状态和上次状态一样及不处理,否则就赋值给当前的state,再通过sync同步。最后会调用onStartChange方法,我们通过这个方法即可拿到对应数据
- Lifecycle以注解方式通知DataListenContainer生命周期变化
1)实现LifecycleObserver接口方式,其本身没有方法,但可以写注解,以回调的方式取到
2)ON_ANY方式进行监听,生命周期所有情况都会调用,太长了就不一一截图了,可以用来查看当前情况。
3)start周期owner与对应event
4. LifeData(可观察的数据存储类)
- 和之前写的联系:做到若处于started或resumed往上的才会更新
- 优势
- 修改方式
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. 实例代码
- 建立
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()}}
}
- 来到PlayPresenter
1)获取实例
2)状态变更处赋值
3)定义类方式进行对象监听
- FlowPlayerControlActivity悬浮按钮中
1)复制上面的代码,改为悬浮界面 - DataListenContainer保存对应状态时一定要赋值,即使下面用的是回调
- 运行结果,通过第一个进入第二个悬浮按钮的活动,都OK