You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓音乐播放器授权音频权限后崩溃求助(API30-33)

问题描述

开发了一款最低API级别30、目标API级别33的音乐播放器应用,安装并授予音频权限后应用崩溃,需重新打开并等待2-3秒加载完音乐文件后才能顺畅运行。

关键错误日志分析

从提供的日志中可定位核心问题:

  1. 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)
    
    属于vivo系统自身反射调用问题,可能触发应用崩溃。
  2. 主线程同步扫描文件导致阻塞:
    授予权限后直接在主线程调用fetchSongs()递归扫描整个外部存储,阻塞UI线程引发无响应甚至崩溃,重启后扫描完成才能正常运行。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:30:41