一.命名规约
该文档为视频学习的个人笔记,图片源自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,1
val 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.补充![在这里插入图片描述](https://img-blog.csdnimg.cn/20201115190843790.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzA0Njcy,size_16,color_FFFFFF,t_70#pic_center)
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=false
override 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=0
const val STATE_LOGIN_SUCCESS=1
const 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,1
val 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.PLAYING
dispatchTitleChange("切换到下一首,标题变化了")
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.PLAYING
dispatchTitleChange("切换到上一首,标题变化了")
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()
}
//对数据进行监听 传一个容器类DLC
private fun initDataListener() {
playerplaysenter.currentmusic.addlistener {
//更新UI 音乐内容发生变化
play_title.text=it?.name
println("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=1
private val size=30
fun getMusic(){
loadState.value=MusicLoadState.LOADING
//请求从Model层获取数据
musicmodel.loadMusicByPage(page,size,object :MusicModel.OnMusicLoadResult{
override fun onSuccess(result: List<Music>) {
musiclist.value=result
loadState.value=if (result.isEmpty()){
MusicLoadState.EMPTY
}else{
MusicLoadState.SUCCESS
}
}
override fun onError(msg: String, code: Int) {
loadState.value=MusicLoadState.ERROR
println("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 比.. 少1
result.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()
}
//对数据进行监听 传一个容器类DLC
private fun initDataListener() {
playerplaysenter.currentmusic.addlistener {
//更新UI 音乐内容发生变化
play_title.text=it?.name
println("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?=null
private 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=state
when(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