android - 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)
解决方案
希望这将有助于像我这样的新手在遇到类似问题时节省一些时间。这个问题与 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)