4、改造MVP--处理异步线程问题

测试音乐列表的功能,在MusicActivity中可以获取音乐列表以及播放状态的数据

V层:MusicActivity,测试用来获取音乐列表,控制音乐播放状态

package com.example.jetpackbysob.musiclisttest1

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.jetpackbysob.R
import kotlinx.android.synthetic.main.activity_music_list.*

/**
 * Project_name:JetPackBySob
 * Created by:ChenFuXu.
 * Date: 2022/5/1 12:50
 */
class MusicActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "MusicActivity"
    }

    private val mMusicPresenter by lazy {
        MusicPresenter.instance
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_music_list)
        initDataListener()
        initViewListener()
    }


    override fun onDestroy() {
        super.onDestroy()
    }

    /**
     * 监听数据的变化
     */
    private fun initDataListener() {
        println("$TAG, cfx initDataListener Thread.currentThread().name = ${Thread.currentThread().name}")
        mMusicPresenter.mMusicList.addListener {
            println("$TAG, cfx initDataListener Thread.currentThread().name = ${Thread.currentThread().name}")
            // 音乐列表数据变化
            println("$TAG, cfx initDataListener mMusicList.size = ${it?.size}")
            get_music_count.text = "共 ${it?.size} 首音乐"

        }
        mMusicPresenter.mLoadState.addListener {
            // 播放状态的数据变化
            println("$TAG, cfx initDataListener mLoadState = $it")
        }
    }

    private fun initViewListener() {
        get_music_list.setOnClickListener {
            // 获取音乐列表
            mMusicPresenter.getMusicList()
        }
    }
}

P层:处理获取音乐列表,控制播放状态相关逻辑

package com.example.jetpackbysob.musiclisttest1

import com.example.jetpackbysob.musiclisttest.domain.MusicBean

/**
 * Project_name:JetPackBySob
 * Created by:ChenFuXu.
 * Date: 2022/5/1 12:52
 */
class MusicPresenter {
    companion object {
        private const val TAG = "MusicPresenter"

        val instance by lazy {
            MusicPresenter()
        }
    }

    enum class MusicLoadState {
        LOADING, EMPTY, SUCCESS, ERROR
    }

    private val mMusicModel by lazy {
        MusicModel()
    }

    val mMusicList = DataListenerContainer<List<MusicBean>>()
    val mLoadState = DataListenerContainer<MusicLoadState>()

    private val page = 1
    private val size = 30

    /**
     * 获取音乐列表
     */
    fun getMusicList() {
        mLoadState.value = MusicLoadState.LOADING
        // 从model层获取音乐列表,这个操作一般是异步操作,子线程执行
        mMusicModel.loadMusicListByPage(page, size, object: MusicModel.OnMusicLoadResult {
            override fun onSuccess(result: List<MusicBean>) {
                // 加载音乐列表成功
                mMusicList.value = result
                mLoadState.value = if (result.isEmpty()) {
                    MusicLoadState.EMPTY
                } else {
                    MusicLoadState.SUCCESS
                }
                println("$TAG getMusicList onSuccess 获取音乐列表成功")

            }

            override fun onError(msg: String, code: Int) {
                // 加载音乐列表失败
                mLoadState.value = MusicLoadState.ERROR
                println("$TAG getMusicList onError msg: $msg code: $code")
            }

        })
    }
}

M层:MusicModel,对音乐内容进行处理

package com.example.jetpackbysob.musiclisttest1

import com.example.jetpackbysob.musiclisttest.domain.MusicBean

/**
 * Project_name:JetPackBySob
 * Created by:ChenFuXu.
 * Date: 2022/5/1 12:52
 */
class MusicModel {
    interface OnMusicLoadResult {
        fun onSuccess(result: List<MusicBean>)

        fun onError(msg: String, code: Int)
    }

    /**
     * 获取音乐列表
     */
    fun loadMusicListByPage(page: Int, size: Int, callback: OnMusicLoadResult) {
        // 异步加载数据
        val result = arrayListOf<MusicBean>()
        Thread {
            for (i in (0 until size)) {
                result.add(
                    MusicBean(
                        name = "音乐名称:$i",
                        cover = "cover 封面:$i",
                        url = "url ==> $i"
                    )
                )
            }
            // 数据加载完成
            // 通知数据更新
            callback.onSuccess(result)
        }.start()
    }
}

MusicBean:音乐实体类 

package com.example.jetpackbysob.musiclisttest1.domain

/**
 * Project_name:JetPackBySob
 * Created by:ChenFuXu.
 * Date: 2022/4/30 22:17
 *
 * 歌曲的实体类对象
 */
data class MusicBean(
    val name: String,
    val cover: String,
    val url: String
) {
}

这里和前文一样使用DataListenerContainer对数据进行监听 

class DataListenerContainer<T> {
    private val mBlocks = arrayListOf<(T?) -> Unit>()
 
    // 数据
    var value: T? = null
        /**
         * 当设置数据时,即表明数据发生变化
         * 就进行通知更新
         */
        set(value: T?) {
            mBlocks.forEach { it.invoke(value) }
        }
 
    // 将匿名函数添加到数据容器中去
    fun addListener(block: (T?) -> Unit) {
        if (!mBlocks.contains(block)) {
            mBlocks.add(block)
        }
    }
 
}

在MusicActivity中只对音乐列表的数据、播放状态的数据(mMusicList、mLoadState)进行监听 

 同前文一样,这里的书写仍然存在问题,一般情况下MusicModel中获取音乐数据是耗时操作,在异步线程执行,所以在P层处理的过程中,最终在V层监听mMusicList、mLoadState中返回后也处于异步线程了,而一般会在V层进行更新UI,所以这里会有应用奔溃的风险,那么如何处理这个问题呢?

可以改写DataListenerContainer,在set方法中进行判断是否是UI线程,如果是UI线程,才更新回调,不是UI线程就切换到UI线程,再进行回调

package com.example.jetpackbysob.musiclisttest

import android.os.Looper

/**
 * Project_name:JetPackBySob
 * Created by:ChenFuXu.
 * Date: 2022/4/30 22:51
 *
 * 数据容器:监听数据的变化
 * 这个类中添加对数据进行监听的功能
 */
class DataListenerContainer<T> {
    private val mBlocks = arrayListOf<(T?) -> Unit>()

    // 数据
    var value: T? = null
        /**
         * 当设置数据时,即表明数据发生变化
         * 就进行通知更新
         */
        set(value: T?) {
            // 判断当前是否是主线程,如果是主线程,继续执行
            // 如果不是主线程,切换到主线程执行
            if (Looper.getMainLooper().thread == Thread.currentThread()) {
                mBlocks.forEach { it.invoke(value) }
            } else {
                // 切换到主线程执行
                App.mHandler.post {
                    mBlocks.forEach { it.invoke(value) }
                }
            }
        }

    // 将匿名函数添加到数据容器中去
    fun addListener(block: (T?) -> Unit) {
        if (!mBlocks.contains(block)) {
            mBlocks.add(block)
        }
    }


}

判断当前线程是否是UI线程的方法?

Looper.getMainLooper().thread == Thread.currentThread()

 这样的话,MVP架构中,存在异步线程更新UI的缺陷也优化处理了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值