【问题标题】:App slows down with calculation of length in seconds of WAV file应用程序以秒为单位计算 WAV 文件的长度变慢
【发布时间】:2013-04-12 16:52:40
【问题描述】:

在文件夹中声音文件的 ListView 中,我想以秒为单位显示文件的长度。我采取的步骤:

  1. 首先,我为 soundFiles 的实例创建一个 ArrayList。
  2. 然后在 for 循环中,我通过 soundFile.setLength(calculateLength(file[i])) 将数据添加到实例中。
  3. 在此之后,我启动了我的 CustomArrayAdapter 并将它应用到我的 listView。
  4. 在我的 CustomArrayAdapter 中应用它:tvFileLength.setText(soundFile.getLength()); (虽然有一个持有人..)

但是由于我这样做,我的应用程序比乌龟还慢! (有 400 个文件) 有什么办法可以解决这个速度?

private int calculateLength(File yourFile)
            throws IllegalArgumentException, IllegalStateException, IOException {
        MediaPlayer mp = new MediaPlayer();
        FileInputStream fs;
        FileDescriptor fd;
        fs = new FileInputStream(yourFile);
        fd = fs.getFD();
        mp.setDataSource(fd);
        mp.prepare(); 
        int length = mp.getDuration();
        length = length / 1000;
        mp.release();
        return length;

    }

   **EDIT**

我拥有的新代码:

活动

myList = new ArrayList<RecordedFile>();

        File directory = Environment.getExternalStorageDirectory();
        file = new File(directory + "/test/");

        File list[] = file.listFiles();

        for (int i = 0; i < list.length; i++) {
            if (checkExtension(list[i].getName()) == true) {

                RecordedFile q = new RecordedFile();
                q.setTitle(list[i].getName());
                q.setFileSize(readableFileSize(list[i].length()));
                            //above is the size in kB, is something else but I 
                            //also might move this to the AsyncTask!


                myList.add(q);
            }
        }
        new GetAudioFilesLength(myList).execute();

异步任务

List<RecordedFile> mFiles = new ArrayList<RecordedFile>();

    public GetAudioFilesLength(List<RecordedFile> theFiles) {
        mFiles = theFiles;
    }

    @Override
    protected String doInBackground(Void... params) {

        File directory = Environment.getExternalStorageDirectory();
        // File file = new File(directory + "/test/");
        String mid = "/test/";

        for (RecordedFile fileIn : mFiles) {

            File file = new File(directory + mid + fileIn.getTitle());
            try {
                int length = readableFileLengthSeconds(file);
                fileIn.setFileLengthSeconds(length);
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // Do something with the length


            // You might want to update the UI with the length of this file
            // with onProgressUpdate so that you display the length of the files
            // in real time as you process them.
        }
        return mid;

    }

    @Override
    protected void onProgressUpdate(Void... values) {

    }

    @Override
    protected void onPostExecute(String result) {
        // Update the UI in any way you want. You might want
        // to store the file lengths somewhere and then update the UI
        // with them here
    }

    /*
     * @Override protected void onPreExecute() { }
     */

    public int readableFileLengthSeconds(File yourFile)
            throws IllegalArgumentException, IllegalStateException, IOException {
        MediaPlayer mp = new MediaPlayer();
        FileInputStream fs;
        FileDescriptor fd;
        fs = new FileInputStream(yourFile);
        fd = fs.getFD();
        mp.setDataSource(fd);
        mp.prepare(); // might be optional
        int length = mp.getDuration();
        length = length / 1000;
        mp.release();
        return length;

    }

太棒了,它可以部分工作,但是!我还有 2 个问题:

  1. 这看起来是否正常且高效?
  2. 它适用于假设我的列表视图中的前 100 个元素,之后它显示 0,它与我假设的 onProgressUpdate 有关,但我不确定如何使其工作。

【问题讨论】:

    标签: android performance listview size duration


    【解决方案1】:

    读取文件以便 MediaPlayer 可以找到持续时间显然需要一些时间。由于您在 UI 线程上运行它,这将减慢整个应用程序的速度。

    我对如何加快进程没有任何建议,但是如果您在使用 AsyncTask 的后台线程中执行此工作,您可以使您的应用程序运行得更加顺畅。这可能看起来像这样:

    private class GetAudioFilesLength extends AsyncTask<Void, Void, Void> {
    
          List<File> mFiles = new ArrayList<File>();
    
          public GetAudioFilesLength(List<File> theFiles){
               mFiles = theFiles;
          }
    
          @Override
          protected String doInBackground(String... params) {
                for(File file : mFiles){
                    int length = calculateLength(file);
                    // Do something with the length
    
                    // You might want to update the UI with the length of this file
                    // with onProgressUpdate so that you display the length of the files
                    // in real time as you process them.
                }
          }      
    
          @Override
          protected void onPostExecute(String result) {
                // Update the UI in any way you want. You might want
                // to store the file lengths somewhere and then update the UI
                // with them here
          }
    
          @Override
          protected void onPreExecute() {
          }
    
          @Override
          protected void onProgressUpdate(Void... values) {
          }
    }   
    

    当您想开始处理时,只需拨打new GetAudioFilesLength(files).execute()

    编辑以回答其他问题:

    1. 它看起来和您的原始代码一样高效。现在的不同之处在于用户仍然可以与您的应用程序交互,因为工作将在后台线程中完成。可能有一种更有效的方法来读取音频文件的长度,但我不知道那是什么。如果您知道采样率和编码,我可以想象您可以编写代码来计算音频的长度,而无需将其加载到 MediaPlayer 中,这需要更长的时间。不过,同样需要其他人提供帮助。

    2. 我不确定我是否理解问题所在,但我想您是在问如何使用 onProgressUpdate 更新 UI 并将长度添加到 ListView?

    您可以将 AsyncTask 生成的中间参数更改为来自 doInBackground 的字符串(或其他)AsyncTask&lt;Void, String, Void&gt;, that tells onProgressUpdate what you will be passing to it. You can then callpublishProgress`,以相应地更新 UI。

    @Override
      protected String doInBackground(Void... params) {
            for(File file : mFiles){
                int length = calculateLength(file);
                // Do something with the length
    
                // You might want to update the UI with the length of this file
                // with onProgressUpdate so that you display the length of the files
                // in real time as you process them.
                publishProgress("The length of " + file.getName() + " is: " + length);
            }
      }      
    
      @Override
      protected void onPostExecute(Void result) {
            // Update the UI in any way you want. You might want
            // to store the file lengths somewhere and then update the UI
            // with them here
      }
    
      @Override
      protected void onProgressUpdate(String... values) {
            // You'll have to implement whatever you'd like to do with this string on the UI
            doSomethingWithListView(values[0]);
      }
    

    【讨论】:

    • 谢谢@mattgmg1990,我今晚或明天试试这个,然后回复你!
    • 没问题,告诉我进展如何!
    • 没问题!我刚刚添加了对您问题的回答。
    • 听起来不错,当您向下滚动并找到尚未设置长度的文件时,它们将为 0,这是因为 AsyncTask 仍在工作且尚未到达他们呢。当您稍后再次上下滚动时,在设置它们的长度后,TextView 将使用新值更新。除非您可以加快文件的处理速度(就像我之前说的,我不确定如何),否则您无法真正改善它。相反,可能会在准备好之前显示一条消息而不是 0,例如“正在计算大小...”
    • @JackCommonw 肯定会,我在想你会将它构建到 onProgressUpdate 中。您可以将计算出长度的文件传递给 onProgressUpdate,使用它来查找要更新的 ListItem,并将 TextView 设置为显示长度。
    猜你喜欢
    • 2011-09-07
    • 2010-11-02
    • 2011-06-05
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多