【问题标题】:Android Handler.postDelayed interrupts sound playingAndroid Handler.postDelayed 中断声音播放
【发布时间】:2014-01-16 22:38:22
【问题描述】:

我正在使用此代码播放声音

     final MediaPlayer mp = MediaPlayer.create(this, R.raw.sound);
        mp.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                mp.release();
            }

        });  

它本身可以正常工作,但是在我添加了一个扩展 ImageView 的动画后出现了问题,该动画以大约 30 毫秒的间隔刷新(通过调用 handler.postDelayed)图像资源以创建动画。问题是当动画开始时,它会终止声音的播放。这是刷新 ImageView 的 Runnable 的代码。

 private Runnable runnable = new Runnable () {


public void run() {
    String name = "frame_" + frameCount;
    frameCount ++;
    int resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
    imageView.setImageResource(resId);  
    if(frameCount < totalFrameCount) {
        mHandler.postDelayed(runnable, interval);
    }       
}

};

我也尝试使用调用 anmiationView.postInvalidate 的线程来执行动画,但是它有同样的问题。请帮忙。谢谢

编辑: 看起来问题是由于动画被调用的时间。之前我在活动的 onActivityResult 中调用它。看起来这不是打电话的正确地方。现在我将动画视图放在一个弹出窗口中并在那里播放,它可以正常工作。不知道具体原因。

【问题讨论】:

  • 如果添加动画导致问题,然后发布该代码。
  • 添加了代码。顺便说一句,我怀疑问题是由于应用程序正在读取声音文件和图像文件。但是图像文件太大了,我无法将它们全部加载到内存中。
  • 是的..如果系统内存不足,它会终止并从其他地方回收它,例如媒体播放器。

标签: android audio android-animation android-mediaplayer


【解决方案1】:

在处理程序的 cmets 中:

"一个 Handler 允许你发送和处理 {@link Message} 和 Runnable 与线程的 {@link MessageQueue} 关联的对象。每个处理程序 实例与单个线程相关联,并且该线程的消息 队列。当你创建一个新的 Handler 时,它被绑定到线程 / 创建它的线程的消息队列——从那时起, 它将消息和可运行文件传递到该消息队列并执行 当它们从消息队列中出来时。”

所以,问题可能是动画和媒体播放操作都在 由哪个线程创建处理程序(假设是主线程)拥有的相同消息队列。

如果动画永远循环播放,那么媒体播放器将几乎没有机会运行。

您可以尝试使用 HandlerThread,该线程将包含一个新的 looper 用于 从它创建的处理程序,添加到该处理程序的所有可运行对象都将在另一个处理程序中运行 单个线程。

动画线程和媒体播放线程应该在不同的线程中运行,而不是 安排在同一个。

希望,它会有所帮助。

HandlerThread 的用法和一些讨论看起来像这样:

How to create a Looper thread, then send it a message immediately?

【讨论】:

  • 我创建了2个线程,一个发布动画的刷新请求,另一个播放声音。现在我也使用 SoundPool 而不是 MediaPlayer,SoundPool 在播放之前将整个音频文件加载到内存中。奇怪的是现在动画只有在声音播放完成后才开始。
【解决方案2】:

可能是因为你错过了安排的代码,我在我的 android 版本 4.4.2 的 nexus 4 上尝试了一下,即使没有任何缓存技术,动画和音乐就像一个魅力...... 这是主要代码:

public class MainActivity extends Activity implements View.OnClickListener {

protected static final String TAG = "test002" ;
protected static final int UPDATE_ANI = 0x0701;
protected static final int UPDATE_END = 0x0702;
protected static final int[] ANI_IMG_IDS = {R.raw.img1, R.raw.img2, R.raw.img3, R.raw.img4,
        R.raw.img5, R.raw.img6, R.raw.img7};
protected static final int[] BTN_IDS = {R.id.btnStart, R.id.btnStop};
protected android.os.Handler aniHandler = null; // async update
protected boolean isAniRunning = false ;
protected int     aniImgIndex = 0 ;
protected ImageView aniImgView = null ;
protected MediaPlayer mediaPly = null ;

// animation timer
class AniUpdateRunnable implements Runnable {
    public void run() {
        Message msg = null ;
        while (!Thread.currentThread().isInterrupted() && isAniRunning) {
            msg = new Message();
            msg.what = UPDATE_ANI;
            aniHandler.sendMessage(msg);

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break ;
            }
        }

        msg = new Message() ;
        msg.what = UPDATE_END ;
        aniHandler.sendMessage(msg) ;
    }
}

protected void prepareMediaPlayer(MediaPlayer mp, int resource_id) {
    AssetFileDescriptor afd = getResources().openRawResourceFd(resource_id);

    try {
        mp.reset();
        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
        afd.close();
        mp.prepare();
    } catch (IllegalArgumentException e) {
        Log.d(TAG, "IlleagalArgumentException happened - " + e.toString()) ;
    } catch(IllegalStateException e) {
        Log.d(TAG, "IllegalStateException happened - " + e.toString()) ;
    } catch(IOException e) {
        Log.d(TAG, "IOException happened - " + e.toString()) ;
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // init : buttons onclick callback
    {
        Button btn;
        int i;
        for (i = 0; i < BTN_IDS.length; i++) {
            btn = (Button) findViewById(BTN_IDS[i]);
            btn.setOnClickListener(this);
        }
    }

    // init : update animation handler callback
    {
        aniHandler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case UPDATE_ANI:
                        updateAniImages();
                        break ;
                    case UPDATE_END:
                        updateAniEnd();
                        break ;
                    default:
                        break;
                }
            }
        };
    }

    // init : prepare image view
    {
        aniImgView = (ImageView)findViewById(R.id.imgAni) ;
        mediaPly = MediaPlayer.create(this, R.raw.happyny) ;
        mediaPly.setLooping(true);
    }
}

protected void updateAniImages() {
    if(aniImgIndex >= ANI_IMG_IDS.length) {
        aniImgIndex = 0 ;
    }

    InputStream is = getResources().openRawResource(ANI_IMG_IDS[aniImgIndex]) ;
    Bitmap bmp = (Bitmap) BitmapFactory.decodeStream(is) ;
    aniImgView.setImageBitmap(bmp);

    aniImgIndex++ ;
}

protected void updateAniEnd() {
    aniImgIndex = 0 ;
    aniImgView.setImageBitmap(null);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btnStart:
            isAniRunning = true ;
            // no re-enter protectiion, should not be used in real project
            new Thread(new AniUpdateRunnable()).start();
            mediaPly.start();
            break;
        case R.id.btnStop:
            isAniRunning = false ;
            mediaPly.stop();
            prepareMediaPlayer(mediaPly, R.raw.happyny);
            break;
        default:
            break;
    }
}
}

主要项目代码和测试apk应该在这里找到:

apk installer

source code

【讨论】:

  • 感谢代码,但问题似乎不是由于动画逻辑,而是调用动画和声音播放的时间。我已经更新了我的问题以提供更多详细信息。
  • 是的,我怀疑毫秒低于 1000 的类似情况,然后音频通话动画等可能会被破坏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多