首页 > 解决方案 > MediaPlayer 在第二次运行时粉碎活动(Kotlin)

问题描述

我真的需要你的专业知识,因为我被这个问题困扰了很长时间。我有一个从模拟器内存​​中获取 MP3 文件 uri 的主要活动。它将 uri 保存在 SQLite 数据库中并读取数据库而不是填充 ListView。当一个 ListView 项目被选中时,主活动启动另一个活动并将其处理为文件的 uri 以播放文件。如果我第一次启动应用程序,它会播放选定的文件就好了。当我关闭应用程序并重新启动它时,我选择了上次保存在 SQLite 中的 ListView 项目。这一次它导致玩家活动在 setDataSource 阶段崩溃。但是,如果我这次从模拟器内存​​中选择一个新的 MP3 文件,它可以正常工作。看起来问题与uri和SQLite有关。我检查了两种情况下的 uri 字符串是否相同。这是主要活动

class MainActivity : AppCompatActivity() {
    
        val REQUEST_FILE = 1
        lateinit var newFilePath: Uri
        lateinit var newFileName: String
        lateinit var AuDB: SQLiteDatabase
        val auName = mutableListOf<String>("")
        var auUri = mutableListOf<Uri>()
        var audioFileValues: ContentValues = ContentValues()
    
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val MyDBHelper = AudioSQLhelper(this)
            AuDB = MyDBHelper.writableDatabase

            val auCursor = AuDB.query("myaudio", arrayOf("name", "uri"), null, null, null, null, "prio", null)
            readcursor(auCursor)
    
            val tbMain = findViewById<Toolbar>(R.id.toolbar1)
            setSupportActionBar(tbMain)
    
            val scAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, auName)
            val lvData = findViewById<ListView>(R.id.lv1)
            lvData.setAdapter(scAdapter)
    
            lvData.setOnItemClickListener { parent, view, position, id ->
    
                val playIntent: Intent = Intent(this, PlayTest::class.java)

                playIntent.putExtra("aname", auName[position])
                playIntent.setData(auUri[position])

                startActivity(playIntent)
            }
        }
    
        override fun onCreateOptionsMenu(menu: Menu?): Boolean {
            menuInflater.inflate(R.menu.menu_main, menu)
            return super.onCreateOptionsMenu(menu)
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when (item.itemId) {
                R.id.action_select_file -> {
    // Select a new audio file
                    val audioIntent: Intent = Intent(Intent.ACTION_GET_CONTENT)
                    audioIntent.setType("audio/mpeg")
                    audioIntent.addCategory(Intent.CATEGORY_OPENABLE)
                    startActivityForResult(audioIntent, REQUEST_FILE)
                }
            }
            return super.onOptionsItemSelected(item)
        }
    
        private fun readcursor(auCursor: Cursor) {
            auCursor.moveToFirst()
            auName.clear()
            auUri.clear()
            while (auCursor.isAfterLast == false) {
                auName.add(auCursor.getString(0))
                auUri.add(Uri.parse(auCursor.getString(1)))
                auCursor.moveToNext()
            }
        }
    
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            if (requestCode == REQUEST_FILE && resultCode == RESULT_OK) {
                newFilePath = data?.data!!
                newFileName = newFilePath.lastPathSegment?.substringAfterLast('/').toString()
    
                if (AuDB.query("myaudio", arrayOf("_id"), "name=?", arrayOf(newFileName), null, null, null).moveToFirst()) {
                    val au_text = "This file has already been added!"
                    val au_duration = Toast.LENGTH_SHORT
                    val toast = Toast.makeText(applicationContext, au_text, au_duration)
                    toast.show()
                } else {
                    // Add the file to the database
                    audioFileValues.put("name", newFileName)
                    audioFileValues.put("uri", newFilePath.toString())
                    audioFileValues.put("prio", 1)
                    addRecord(audioFileValues)
    
                }
            }
        }
        // Add new file at the beginning if the list
        fun addRecord(content: ContentValues) {
            val MyDBHelper = AudioSQLhelper(this)
            val AuDB = MyDBHelper.writableDatabase
       // Increase others file priority fiels by one
            AuDB.execSQL("UPDATE myaudio SET prio = prio + 1")

            AuDB.insert("myaudio", null, content)
            val auCursor = AuDB.query("myaudio", arrayOf("name", "uri"), null, null, null, null, "prio", null)

            readcursor(auCursor)
    
            val scAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, auName)
            val lvData = findViewById<ListView>(R.id.lv1)
            lvData.setAdapter(scAdapter)
        }
    }

这是玩家活动

class PlayTest : AppCompatActivity() {

    var mPlayer = MediaPlayer()
    lateinit var auUri: Uri

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_playtest)

        val caption = findViewById<TextView>(R.id.txtAUName)
        caption.text = intent.getStringExtra("aname")
        auUri = intent.data!!
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun playClick(view: View) {
        when (view.id) {
            R.id.btnAStart -> {
                mPlayer = MediaPlayer().apply {
                    setAudioAttributes(
                        AudioAttributes.Builder()
                            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                            .setUsage(AudioAttributes.USAGE_MEDIA)
                            .build()
                    )
                    setDataSource(this@PlayTest, auUri)
                    prepare()
                }
                mPlayer.start()
            }
            R.id.btnAStop -> {
                mPlayer.pause()
            }
            R.id.btnAClose -> {
                    mPlayer.release()
                    finish()

            }
        }
    }
}

这是Logcat。对我来说没什么

2021-04-03 01:10:51.193 11233-11233/com.example.test1 E/MediaPlayerNative: Unable to create media player
2021-04-03 01:10:51.195 11233-11233/com.example.test1 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.test1, PID: 11233
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
        at android.view.View.performClick(View.java:7448)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
        at android.view.View.performClick(View.java:7448) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119) 
        at android.view.View.performClickInternal(View.java:7425) 
        at android.view.View.access$3600(View.java:810) 
        at android.view.View$PerformClick.run(View.java:28305) 
        at android.os.Handler.handleCallback(Handler.java:938) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
     Caused by: java.io.IOException: setDataSource failed.: status=0x80000000
        at android.media.MediaPlayer.nativeSetDataSource(Native Method)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1175)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1162)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1079)
        at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1004)
        at com.example.test1.PlayTest.playClick(PlayTest.kt:46)
        at java.lang.reflect.Method.invoke(Native Method) 
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409) 
        at android.view.View.performClick(View.java:7448) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119) 
        at android.view.View.performClickInternal(View.java:7425) 
        at android.view.View.access$3600(View.java:810) 
        at android.view.View$PerformClick.run(View.java:28305) 
        at android.os.Handler.handleCallback(Handler.java:938) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

标签: androidandroid-studioandroid-mediaplayer

解决方案


希望这将有助于像我这样的新手在遇到类似问题时节省一些时间。这个问题与 Android 允许完全访问处理用户选择的文件的 Uri 有关,在我的例子中是通过 Intent(Intent.ACTION_GET_CONTENT) 。在您的小工具或模拟器重新启动后,此 Uri 没有任何权限。要通过 Uri 保持文件可访问,您需要获取持久性 URI 权限。这是官方指南的链接。我已将此代码片段添加到我的 onActivityResult 函数中,在那里我从 Intent 接收文件 Uri。

        val contentResolver = applicationContext.contentResolver
        val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION
        contentResolver.takePersistableUriPermission(newFilePath, takeFlags) 

推荐阅读