安卓音乐播放器授权音频权限后崩溃求助(API30-33)
问题描述
开发了一款最低API级别30、目标API级别33的音乐播放器应用,安装并授予音频权限后应用崩溃,需重新打开并等待2-3秒加载完音乐文件后才能顺畅运行。
关键错误日志分析
从提供的日志中可定位核心问题:
- vivo系统性能优化模块空指针异常:
属于vivo系统自身反射调用问题,可能触发应用崩溃。W/System.err: java.lang.NullPointerException: null receiver W/System.err: at java.lang.reflect.Method.invoke(Native Method) W/System.err: at com.vivo.framework.vperf.VivoScrollOptimizer.setMotionType(VivoScrollOptimizer.java:397) - 主线程同步扫描文件导致阻塞:
授予权限后直接在主线程调用fetchSongs()递归扫描整个外部存储,阻塞UI线程引发无响应甚至崩溃,重启后扫描完成才能正常运行。 - MediaPlayer生命周期管理不当:
存在Surface has already been released异常,说明MediaPlayer资源未正确释放。
解决方案
1. 异步执行音乐文件扫描,避免阻塞主线程
将文件扫描逻辑移至后台线程,使用AndroidX推荐的Coroutine实现:
修改MainActivity代码
package com.techxperts.iplay; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.lifecycleScope; import android.Manifest; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.ProgressBar; import com.karumi.dexter.Dexter; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.single.PermissionListener; import java.io.File; import java.util.ArrayList; import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.launch; import kotlinx.coroutines.withContext; public class MainActivity extends AppCompatActivity { private ListView listView; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = findViewById(R.id.listView); progressBar = findViewById(R.id.progressBar); // 用于显示加载状态 Dexter.withContext(this) .withPermission(Manifest.permission.READ_MEDIA_AUDIO) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) { // 在IO线程执行文件扫描 lifecycleScope.launch(Dispatchers.IO) { ArrayList<File> mySongs = fetchSongs(Environment.getExternalStorageDirectory()); // 回到主线程更新UI withContext(Dispatchers.Main) { progressBar.setVisibility(View.GONE); updateSongList(mySongs); } } } @Override public void onPermissionDenied(com.karumi.dexter.listener.PermissionDeniedResponse permissionDeniedResponse) { // 可添加权限拒绝提示逻辑 } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) { permissionToken.continuePermissionRequest(); } }) .check(); } private void updateSongList(ArrayList<File> mySongs) { String[] items = new String[mySongs.size()]; for(int i=0; i<mySongs.size(); i++){ items[i] = mySongs.get(i).getName().replace(".mp3", ""); } ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, items); listView.setAdapter(adapter); listView.setOnItemClickListener((parent, view, position, id) -> { Intent intent = new Intent(MainActivity.this, PlaySong.class); String currentSong = listView.getItemAtPosition(position).toString(); intent.putExtra("songList", mySongs); intent.putExtra("currentSong", currentSong); intent.putExtra("position", position); startActivity(intent); }); } public ArrayList<File> fetchSongs(File file){ ArrayList<File> arrayList = new ArrayList<>(); File [] songs = file.listFiles(); if(songs !=null){ for(File myFile: songs){ if(!myFile.isHidden() && myFile.isDirectory()){ arrayList.addAll(fetchSongs(myFile)); } else{ if(myFile.getName().endsWith(".mp3") && !myFile.getName().startsWith(".")){ arrayList.add(myFile); } } } } return arrayList; } }
添加进度条到布局文件
在activity_main.xml中添加加载提示:
<ProgressBar android:id="@+id/progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="visible"/>
2. 捕获vivo系统异常避免崩溃
创建自定义Application类过滤系统异常:
package com.techxperts.iplay; import android.app.Application; import android.util.Log; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { // 过滤vivo系统性能优化模块的异常 if (throwable.getStackTrace() != null && throwable.getStackTrace().length > 0) { StackTraceElement element = throwable.getStackTrace()[0]; if (element.getClassName().contains("com.vivo.framework.vperf")) { Log.w("VivoPerf", "忽略vivo系统异常", throwable); return; } } throwable.printStackTrace(); }); } }
在AndroidManifest.xml中注册:
<application android:name=".MyApp" ...> ... </application>
3. 优化MediaPlayer生命周期管理
在PlaySongActivity中正确释放资源:
@Override protected void onPause() { super.onPause(); if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } } @Override protected void onDestroy() { super.onDestroy(); if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } }
内容的提问来源于stack exchange,提问作者Gulshan Kumar Singh
相关产品推荐
相关产品推荐

