即使从线程调用,Android MediaPlayer也会挂起UI

时间:2013-10-17 21:35:02

标签: android multithreading android-mediaplayer

我一直试图在今天整天解决这个问题,在其他日子里花费太多时间。但今天我至少能够改善这个问题。

我有一个带有搞乱标题的.acc音频文件,让我们调用它"BadFile":根据我在前几天注意到的android本机C代码的logcat转储,标题说该文件有44.100Hz ,但数据本身有0Hz。

我有一个文件的arraylist,表示一个播放列表和一个int指针,用于定义在给定时刻播放的歌曲。当我在播放列表中排队一些歌曲时,可以说GoodFile1,GoodFile2,BadFile,GoodFile3,GoodFile4等...,指针设置为0,GoodFile1开始播放。

//这是从Thread.currentThread()。getId()== 1

调用的
 private void play() {
            Thread t = new Thread(new Runnable() {

                @Override
                public void run() {
                    playCicle();
                }
            });
            t.start();
        }

private synchronized void playCicle() {
        Log.e("THREAD" , "T:" + Thread.currentThread().getId());
        Log.e("TRYING TO PLAY", playlist.get(playPointer).getFile().getAbsolutePath());
        try {
            if(progressSenderRunnable != null) {
                progressSenderRunnable.stop();
            }
            if(myMP != null) {
                myMP.release();
            }
            myMP = new MediaPlayer();
            myMP.setAudioStreamType(AudioManager.STREAM_MUSIC);
            myMP.setDataSource(mService, Uri.parse(Uri.encode(playlist.get(playPointer).getFile().getAbsolutePath())));
            setOnPrepareListener();
            myMP.prepare();
            setOnCompletionListener();
            setOnErrorListener();
            myMP.start();
            sendImPlaying();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            return;
        } catch (SecurityException e) {
            e.printStackTrace();
            return;
        } catch (IllegalStateException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        Log.e("METHOD " , "EXITING");
    }

private void setOnCompletionListener() {
        myMP.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer arg0) {
                Log.e("onCompletionthread" , "T: "+Thread.currentThread().getId());
                Thread t = new Thread(new Runnable() {

                    @Override
                    public void run() {
                        if(!userSetPlayPointerNext && !userSetPlayPointerPrevious) {
                            playPointer++;
                        }
                        userSetPlayPointerNext = false;
                        userSetPlayPointerPrevious = false;
                        if(playPointer < playlist.size()) {
                            play();
                        }
                        else {
                            if(progressSenderRunnable != null) {
                                progressSenderRunnable.stop();
                            }
                            if(myMP != null) {
                                myMP.release();
                            }
                            sendImStopped();
                        }
                    }
                });
                t.start();
            }
        });
    }

当它开始播放第一首歌时,我点击下一个按钮,导致ID为1的线程调用此下一个方法。

void playNextSong() {
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                if(isPlaying() && playPointer < playlist.size() - 1) {
                    playPointer++;
                    userSetPlayPointerNext = true;
                    myMP.seekTo(myMP.getDuration() - 1);
                }
            }
        });
        t.start();
    }

GoodFile2开始播放。 然后我开始垃圾邮件下一个按钮,UI就会挂起。在这个挂起后,我可以垃圾邮件下一个按钮就好了(因为我们已经过了BadFile)。

10-17 21:18:23.734: E/THREAD(24021): T:41
10-17 21:18:23.734: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/01_Compay Segundo - Te Apartes De Mi.mp3
10-17 21:18:23.744: E/METHOD(24021): EXITING
10-17 21:18:23.744: E/PREPARED(24021): PREPARED
10-17 21:18:24.364: E/Service rcv(24021): 17
10-17 21:18:24.364: E/back in handler(24021): 1
10-17 21:18:24.384: E/onCompletionthread(24021): T: 1
10-17 21:18:24.384: E/THREAD(24021): T:45
10-17 21:18:24.384: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/02 - Blue.aac
10-17 21:18:24.394: E/METHOD(24021): EXITING
10-17 21:18:24.724: E/PREPARED(24021): PREPARED
10-17 21:18:28.914: E/onCompletionthread(24021): T: 1
10-17 21:18:28.924: E/THREAD(24021): T:48
10-17 21:18:28.924: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/02_Ibrahím Ferrer - Ay Candela.mp3
10-17 21:18:28.944: E/METHOD(24021): EXITING
10-17 21:18:29.074: E/PREPARED(24021): PREPARED

At:10-17 21:18:23.734你可以看到GoodFile1几乎被ID为41的线程播放

At:10-17 21:18:24.364:您可以看到服务恢复playNextSong()的op_code。

At:10-17 21:18:24.364 ::你可以看到从playNextSong()

的电话回来的主线程

同时,ID为45的线程正在加载BadFile并退出playNextSong()方法。

现在这里是悬念:

10-17 21:18:24.724:E / PREPARED(24021):PREPARED

10-17 21:18:28.914:E / onCompletionthread(24021):T:1

在这4s期间,我的UI无法使用。我所看到的是,即使没有从主线程调用setOnCompletion侦听器,它也是在侦听器调用时调用的主线程,尽管我不确定这是否与挂起有关。

此刻我可能有太多不必要的线程,但我试图尽可能远离主线程完成大部分工作。我将来会改变它。

此时我认为synchronized关键字是无用的,因为我正在使用completionListener来启动新线程来播放下一首歌曲。

如果有人对我所犯的错误有所了解,那就是让我支付那些4s,我会更加乐观!

修改

我正在谈论的控制台转储,这个警告不会出现在我的软件包logcat过滤器中:

10-18 07:46:37.055: E/THREAD(10772): T:21
10-18 07:46:37.055: E/TRYING TO PLAY(10772): /mnt/sdcard/Music/BadFileTest/02 - Blue.aac
10-18 07:46:37.075: I/StagefrightPlayer(157): setDataSource('/mnt/sdcard/Music/BadFileTest/02 - Blue.aac')
10-18 07:46:37.625: V/OMXCodec(157): Attempting to allocate OMX node 'OMX.Nvidia.aac.decoder'
10-18 07:46:37.625: V/OMXCodec(157): Attempting to allocate OMX node 'OMX.TI.AAC.decode'
10-18 07:46:37.625: V/OMXCodec(157): Successfully allocated software codec 'AACDecoder'
10-18 07:46:37.625: E/PREPARED(10772): PREPARED
10-18 07:46:37.625: E/start(10772): before
10-18 07:46:37.625: W/AACDecoder(157): Sample rate was 44100 Hz, but now is 0 Hz
10-18 07:46:37.625: E/start(10772): after
10-18 07:46:37.635: W/AACDecoder(157): Disable AAC+/eAAC+ since upsampling factor is 1
10-18 07:46:37.635: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.635: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence

奇怪的是,这个编解码器错误警告的时间大约是4s,只是主线程挂起的时间。

1 个答案:

答案 0 :(得分:0)

问题不在于我的客户端代码,它是在尝试解码BadFile时占用CPU的AAC解码器。刚用CPU监视器确认了这一点。因此它不是我的主要线程,它是整个系统。