Android MediaPlayer 是多线程的吗?

Is Android MediaPlayer multithreaded?

我们可以从后台线程创建和使用 Android MediaPlayer 吗?

我问是因为奇怪的是,所有回调事件(如 OnErrorOnPreparedOnVideoSizeChanged 等)都在主 UI 线程中触发,并且即使 MediaPlayer 是在后台线程中创建(和使用)的,也会发生这种情况。

您可以从后台线程创建和使用 MediaPlayer。但是要在后台线程上接收回调,该线程必须实现 Looper。如果线程没有 Looper 将在主 (UI) 线程上调用回调。

来自 Android 文档 MediaPlayer:

Callbacks

Applications may want to register for informational and error events in order to be informed of some internal state update and possible runtime errors during playback or streaming. Registration for these events is done by properly setting the appropriate listeners (via calls to setOnPreparedListener(OnPreparedListener)setOnPreparedListener, setOnVideoSizeChangedListener(OnVideoSizeChangedListener)setOnVideoSizeChangedListener, setOnSeekCompleteListener(OnSeekCompleteListener)setOnSeekCompleteListener, setOnCompletionListener(OnCompletionListener)setOnCompletionListener, setOnBufferingUpdateListener(OnBufferingUpdateListener)setOnBufferingUpdateListener, setOnInfoListener(OnInfoListener)setOnInfoListener, setOnErrorListener(OnErrorListener)setOnErrorListener, etc).

In order to receive the respective callback associated with these listeners, applications are required to create MediaPlayer objects on a thread with its own Looper running (main UI thread by default has a Looper running).


观察在有或没有 Lopper 的线程上创建 MediaPlayer 之间差异的最基本示例:

        HandlerThread thread = new HandlerThread("mp") {
//        Thread thread = new Thread() {

        @Override
        public void onLooperPrepared() {
//        public void run() {
            Log.d("XAPP", "BG Thread " + Long.toString(Thread.currentThread().getId()));
            MediaPlayer player = MediaPlayer.create(MainActivity.this, R.raw.sound);
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
            {
                @Override
                public void onPrepared(MediaPlayer mp)
                {
                    Log.d("XAPP", "onPrepared " + Long.toString(Thread.currentThread().getId()));
                    mp.start();
                }
            });

            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
            {
                @Override
                public void onCompletion(MediaPlayer mp)
                {
                    Log.d("XAPP", "onCompletion " + Long.toString(Thread.currentThread().getId()));
                }
            });

        }};

        thread.start();

HandlerThread 有一个 Looper 和 运行 上面的代码将导致以下 logcat 输出。所有回调都在后台线程上执行

01-11 14:33:04.122 5099-5099/xxx D/XAPP: UI Thread 1
01-11 14:33:04.122 5099-5173/xxx D/XAPP: BG Thread 416
01-11 14:33:04.152 5099-5173/xxx D/XAPP: onPrepared 416
01-11 14:33:05.133 5099-5173/xxx D/XAPP: onCompletion 416

切换到 Thread 实现(取消注释 Thread() 行和 run() 行,并注释 HandlerThread()onLooperPrepared() 行)没有 Looper 将在 logcat 之后产生,其中回调在主线程

的上下文中执行
01-11 14:31:45.706 4916-4916/xxx D/XAPP: UI Thread 1
01-11 14:31:45.706 4916-4994/xxx D/XAPP: BG Thread 413
01-11 14:31:45.736 4916-4916/xxx D/XAPP: onPrepared 1
01-11 14:31:46.717 4916-4916/xxx D/XAPP: onCompletion 1