一个kotlin实现的音乐播放器

       现在kotlin成为android开发的主流开发语言,相对于java,可谓有过之而无不及,例如处理了空指针,简化了代码,使用精简的语法代替了相对繁琐的语法,采用协程代替了线程的使用,匿名函数更方便的处理逻辑.....单从这些方面就可以知道kotlin相对于java更精简,更安全,更方便,那么它是如何体现这些的呢?废话不多说,以我写的一个本地音乐播放器为例,从代码方面去更好的理解这些优势。

      这个播放器是一个播放手机本地文件中的mp3的音乐文件的播放器,其中的主要的步骤是:

1:进入主页面,onCreate中循环遍历手机本地文件夹查找所有mp3文件

2:将获取到的mp3文件的路径放入MutableList<String>中,通过

MutableLiveData<MutableList<String>>的方式将数据返回给到activity

3:通过Recyclerview设置adapter将数据展示出来

4:通过MediaPlayer将选中文件播放出来

开发架构模式:MVVM(kotlin+DataBinding+LiveData+协程)

下面从代码方面分析以上四点各点的知识点:(下面的第一点对应上面的第一点,以此类推)

1:进入主页面,需要先动态获取文件权限

(1)申请文件读写权限:

 /**
     * 申请文件读写权限
     */
    fun requestFilePermission() {
        if (!checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) || !checkPermission(
                Manifest.permission.READ_EXTERNAL_STORAGE)) {
            activity.requestPermissions(
                arrayOf(
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
//                    Manifest.permission.MANAGE_EXTERNAL_STORAGE
                ), REQUEST_WRITE_EXTERNAL_STORAGE
            )
        } else {
            onFilePermissionCallback(true)
        }
    }

(2) 通过回调确认是否获取动态权限:

/**
     * 文件权限获取结果的回调
     */

    fun onRequestResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when (requestCode) {
            REQUEST_WRITE_EXTERNAL_STORAGE -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //已经申请权限
                    onFilePermissionCallback(true)
                } else {
                    //申请权限失败
                    onFilePermissionCallback(false)
                }
            }
        }

    }

注意此处获取到的文件权限结果是通过匿名函数

onFilePermissionCallback: (result: Boolean) -> Any

的方式(类似于java的回调接口)返回的,使用方式如下(举例):

  //调用函数
    fun a(b:Int,c:String,back:(d:Int)->Unit){
        back(b)
    }
    //函数实现
    val result=a(5,"daxiang"){
        //此处为back返回的值为b,因此为5,可用it来操作
    }

(3)获取到文件读写权限后,遍历手机本地文件夹,查询所有.mp3文件,如下:

 /**
     * 查找本地音乐文件
     */
    fun findMusicFile(onMusicListCallback: (MutableList<String>) -> Unit) {
        val rootUrl: String = Environment.getExternalStorageDirectory().path   //+"/gx"
        Log.d("dx", "rooturl=$rootUrl")
        val rootFile: File = File(rootUrl)
        mainScope.launch {
            onMusicListCallback(withContext(Dispatchers.IO) {
                musics.clear()
                loopFile(rootFile)
                musics
            })
        }

    }

    /**
     * 循环遍历文件夹获取文件后缀为.mp3的文件
     */
    private fun loopFile(file: File) {
        val files = file.listFiles()
        if (files != null) {
            files.forEach {
                if (it.isDirectory) {
                    Log.d("dx", "is Directory=${it.path}")
                    loopFile(it)
                } else {
                    Log.d("dx", "is not Directory=${it.path}")
                    if (it.path.contains(".mp3")) {
                        Log.d("dx", "add success")
                        musics.add(it.path)
                    }
                }
            }
        }
    }

2:将获取到的文件数据返回给到activity

 /**
     * 监听音乐数据
     */
    fun listenMusicData() {
        musicViewModel.getMusicData().observe(this, Observer {
            Log.d("dx", "mainactivity musicdata $it")
            refreshData(it)
        })
    }

3:将数据通过adapter展示出来:

/**
     * 刷新数据列表
     */
    private fun refreshData(result: MutableList<String>) {
        musicData=result
        musicAdapter.refreshData(result,-1)
    }

4:通过MediaPlayer播放对应选项的歌曲

override fun play(position: Int) {
//        musicPlayer.release()
     //判断是否点击播放按钮
        if (position == -1) {
            if (!musicPlayer.isPlaying) {
                if (isPrepared) {
                    musicPlayer.start()
                } else {
                    play(0)
                }
            }
            return
        }

        var resultUrl = data[position]
        Log.d("dx", "resultUrl=$resultUrl")
        if (resultUrl.isEmpty()) {
            return
        }
        if (musicPlayer.isPlaying && currentPosition == position) {
            return
        } else {
            musicPlayer.reset()
        }
        musicPlayer.setDataSource(resultUrl)
        musicPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC)
        musicPlayer.prepareAsync()
        musicPlayer.setOnPreparedListener(MediaPlayer.OnPreparedListener {
            isPrepared = true
            musicPlayer.start()
        })
        musicPlayer.setOnCompletionListener {
            next()
        }
        currentPosition = position
        onPositionCallback(position)

    }

通过以上可以看出

1):匿名函数代替java接口回调方式(上面已讲)

2):协程的使用:

mainScope.launch {//协程作用域
    onMusicListCallback(withContext(Dispatchers.IO) {//运行在IO线程中
        musics.clear()
        loopFile(rootFile)
        musics //最后一行代表该匿名函数返回值
    })
}

3):语法的简化:

很多,这里体现的是

files.forEach {...}代替了java的for循环,匿名函数代替了java接口回调,协程
代替了java线程,对象属性的调用可以直接使用而不需要通过get,set方法获取等等
​​​​​​​(更多的语法使用优势需要大家去查看熟悉kotlin语法)

这些步骤我只贴了关键位置的代码,整个完整的代码我上传到了github上面,需要的可以使用

git copy下来查看,感谢。完整代码:https://github.com/daxiangzaici214703306/KtPlayericon-default.png?t=N176https://github.com/daxiangzaici214703306/KtPlayer

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值