Android MediaPlayer 是多线程的吗?
Is Android MediaPlayer multithreaded?
我们可以从后台线程创建和使用 Android MediaPlayer 吗?
我问是因为奇怪的是,所有回调事件(如 OnError
、OnPrepared
、OnVideoSizeChanged
等)都在主 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
我们可以从后台线程创建和使用 Android MediaPlayer 吗?
我问是因为奇怪的是,所有回调事件(如 OnError
、OnPrepared
、OnVideoSizeChanged
等)都在主 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