将文件转换为字节数组会减慢应用程序的速度

Converting file to byte array slowing down the app

我正在使用这个库:https://github.com/alxrm/audiowave-progressbar 来提供类似 Soundcloud 音乐播放器的音频波形效果。

我是这样实现的:

byte[] data = convert(songsList.get(currentSongIndex).get("songPath"));

final AudioWaveView waveView = (AudioWaveView) findViewById(R.id.wave);

waveView.setScaledData(data);
waveView.setRawData(data, new OnSamplingListener() {
    @Override
    public void onComplete() {

    }
});

waveView.setOnProgressListener(new OnProgressListener() {
    @Override
    public void onStartTracking(float progress) {

    }

    @Override
    public void onStopTracking(float progress) {

    }

    @Override
    public void onProgressChanged(float progress, boolean byUser) {

    }
});

这是使用所述文件路径将文件转换为字节数组的方法

public byte[] convert(String path) throws IOException {

    FileInputStream fis = new FileInputStream(path);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    byte[] b = new byte[1024];

    for (int readNum; (readNum = fis.read(b)) != -1;) {
        bos.write(b, 0, readNum);
    }

    byte[] bytes = bos.toByteArray();

    return bytes;
}

可能是因为没有异步获取数组导致的问题,但我不确定。

谁能帮我解决这个问题?

编辑:正如@commonsware 正确建议的那样,我这样做了:

 class AudioWave extends AsyncTask<String,Integer,String>{
     private Context mContext;
     private View rootView;
     public MusicPlayerActivity m;
     final AudioWaveView waveView = (AudioWaveView) m.findViewById(R.id.wave);

     public AudioWave(MusicPlayerActivity m1){
         m = m1;
     }
     // Runs in UI before background thread is called
     @Override
     protected void onPreExecute() {
         super.onPreExecute();

         // Do something like display a progress bar
     }
    byte[] b2;
     // This is run in a background thread
     @Override
     protected String doInBackground(String... params){
         // get the string from params, which is an array
         String path = params[0];
         try {
             FileInputStream fis = new FileInputStream(path);
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             byte[] b = new byte[1024];
             try {
                 for (int readNum; (readNum = fis.read(b)) != -1; ) {
                     bos.write(b, 0, readNum);
                 }

             }catch(IOException e){

             }

         byte[] bytes = bos.toByteArray();
             b2=bytes;
             new MusicPlayerActivity().setData(b2);
         }
         catch (FileNotFoundException e){

         }
         return "lol";
     }

     // This is called from background thread but runs in UI
     @Override
     protected void onProgressUpdate(Integer... values) {

         super.onProgressUpdate(values);

         // Do things like update the progress bar
     }

     // This runs in UI when background thread finishes
     @Override
     protected void onPostExecute(String result) {
         waveView.setScaledData(b2);
         waveView.setRawData(b2, new OnSamplingListener() {
             @Override
             public void onComplete() {

             }
         });

         waveView.setOnProgressListener(new OnProgressListener() {
             @Override
             public void onStartTracking(float progress) {

             }

             @Override
             public void onStopTracking(float progress) {

             }

             @Override
             public void onProgressChanged(float progress, boolean byUser) {

             }
         });

         super.onPostExecute(result);
         // Do things like hide the progress bar or change a TextView
     }
 }

按照您的方式加载本地文件是完全低效的:

  1. 您正在使用一个非常小的 1024 字节缓冲区。缓冲区越大速度越快,尤其是考虑到智能手机使用的闪存在内部使用更大的内存块。

  2. 您正在使用 ByteArrayOutputStream。这是一个动态增长的字节数组。每次它增长到超过当前缓冲区的大小时,所有内容都会从旧缓冲区复制到新缓冲区。由于它是一个本地文件,您知道文件大小,因此您可以使用完整大小初始化缓冲区。 但是你不再需要 ByteArrayOutputStream 了。

因此您可以使用一个读取命令简单地加载文件:

File f = new File(path);
int size = (int) g.length();
byte[] fileData = new byte[size];
try (DataInputStream in = new DatInpuStream(FileInputStream(f)))) {
    in.readFully(fileData);
}

无论如何,当您将其完全加载到内存中时,这仅适用于 "small" 个文件。如果您的 AudioWaveView 支持它,您应该使用流式处理方法:将 FileInputStream 与 BufferedInputStream 包装在一起,并将其缓冲区大小设置为 512KB 左右。

在主应用程序线程上执行 I/O 会在 I/O 期间冻结您的 UI,这意味着您的 UI 将没有响应。将 I/O 移动到后台线程对于 well-behaved UI.

是必要的

Android 坚持认为我们只能从主应用程序线程安全地更新 UI,这意味着您需要使用 AsyncTaskThreadrunOnUiThread()、RxJava/RxAndroid 或其他安排后台工作并在后台工作完成后更新 UI 的方法。

对于您的情况,在后台线程上执行 convert() 逻辑,并在主应用程序线程上更新 waveView 应该会有所帮助。

请使用此功能提高性能---

private static byte[] readBytesFromFile(String filePath) {

        FileInputStream fileInputStream = null;
        byte[] bytesArray = null;

        try {

            File file = new File(filePath);
            bytesArray = new byte[(int) file.length()];

            //read file into bytes[]
            fileInputStream = new FileInputStream(file);
            fileInputStream.read(bytesArray);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        return bytesArray;

    }