【问题标题】:How to maintain single instance of MediaPlayer [Android]如何维护 MediaPlayer 的单个实例 [Android]
【发布时间】:2012-05-22 15:41:45
【问题描述】:

我正在使用 android 媒体播放器类在我的 android 应用程序中播放通知声音。

MediaPlayer player = MediaPlayer.create(getApplicationContext(), R.raw.notify);
player.setLooping(false);
player.start();

我需要在不同的活动中播放不同的通知声音,所以每次我需要播放声音时我都需要创建媒体播放器实例然后我需要说开始。

但不是这样做,我如何在整个应用程序中维护媒体播放器的单个实例并在所有活动中使用它来播放声音。

有人可以建议我更好的实现方式吗? 从我的角度来看,我将创建一个单例类,并在该类中添加所有与 MediaPlayer 相关的函数。

谢谢。

【问题讨论】:

  • 感谢重播,是否每次播放完媒体文件时我都需要调用player.release(),或者我可以在退出应用程序后或我的单例类获取时调用它被摧毁。当我调用 release 时,我需要再次调用 create。

标签: android android-mediaplayer


【解决方案1】:

详细说明单例注意事项:

注意:这里也解决了连续创建一系列播放器(比如 20 个 mediaPlayers)时 Audioservice 失败的问题。

创建播放器:单例类应该创建另一个线程来处理媒体播放器操作(不是主 UI 线程)

Create Player Runnable:这个线程(由单例实例创建的应该被赋予后台优先级,在创建逻辑之前延迟“Thread.sleep(500);”以允许AudioService-由MediaPlayer.create()- 完成它的工作,因为后面的方法会立即返回。

创建播放器可运行代码

/**
 * Created by George hannuneh on 10/12/2015.
 * Holds the background work for creating a media player
 */
public class CreatePlayerRunnable implements Runnable {

static final int CREATE_STATE_FAILED = -1;
static final int CREATE_STATE_STARTED= 0;
static final int CREATE_STATE_COMPLETED= 1;
private static final String TAG ="CreatePlayerRunnable";
private static int sRunnablesCount = 1;


final TaskRunnableCreatePlayerMethods mPlayerTask;

/**
 *
 * An interface that defines methods that PlayerCreationTask implements. An instance of
 * CreatePlayerTask passes itself to an CreatePlayerRunnable instance through the
 * CreatePlayerRunnable constructor, after which the two instances can access each other's
 * variables.
 */
interface TaskRunnableCreatePlayerMethods {
    /**
     * Sets the Thread that this instance is running on
     * @param currentThread the current Thread
     */
    void setCreatePlayerThread(Thread currentThread);

    Context getActivity();

    Uri getMediaUri();

    void handleCreationState(int createStateFailed);

    void setPlayer(MediaPlayer returnMediaPlayer);

    String getPlayerId();

    MediaPlayer getPlayer();
}
/**
 * This constructor creates an instance of CreatePlayerRunnable and stores in it a reference
 * to the CreatePlayerTask instance that instantiated it.
 *
 * @param createPlayerTask The CreatePlayerTask
 */
CreatePlayerRunnable(TaskRunnableCreatePlayerMethods createPlayerTask) {
    mPlayerTask = createPlayerTask;
}
@Override
public void run() {
    /*
     * Stores the current Thread in the CreatePlayerTask instance,
     * so that the instance
     * can interrupt the Thread.
     */
    mPlayerTask.setCreatePlayerThread(Thread.currentThread());
    // Moves the current Thread into the background
    android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
    MediaPlayer returnMediaPlayer = null;

    try {

        Thread.sleep(500);
        // Before continuing, checks to see that the Thread hasn't
        // been interrupted
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        returnMediaPlayer = MediaPlayer.create(mPlayerTask.getActivity(), mPlayerTask.getMediaUri());
        if (returnMediaPlayer == null) {
            Log.e("CreatePlayerRunnable", mPlayerTask.getMediaUri()+ " - failed to create player");
            return;
        }
        PlayerEventsHandler playerEvents = new PlayerEventsHandler(mPlayerTask.getPlayerId());
        returnMediaPlayer.setLooping(true);
        returnMediaPlayer.setOnCompletionListener(playerEvents);
        returnMediaPlayer.setOnErrorListener(playerEvents);
        returnMediaPlayer.setVolume(0f, 0f);
        returnMediaPlayer.start();
    } catch (InterruptedException e1) {
        // Does nothing
    } catch(Exception e)
    {
        returnMediaPlayer = null;
        e.printStackTrace();
    }
    finally {
        if(MainActivity.DEBUG_MODE_ENABLED){
            Log.d(TAG, "end of runnable: "+ sRunnablesCount++);
        }
        if (null == returnMediaPlayer){
            mPlayerTask.handleCreationState(CREATE_STATE_FAILED);
        } else {
            mPlayerTask.setPlayer(returnMediaPlayer);
            // Reports a status of "completed"
            mPlayerTask.handleCreationState(CREATE_STATE_COMPLETED);
        }

        // Sets the current Thread to null, releasing its storage
        mPlayerTask.setCreatePlayerThread(null);
        // Clears the Thread's interrupt flag
        Thread.interrupted();
    }

}

}

【讨论】:

  • 如果我从服务运行媒体播放器,是否需要单独运行?
  • 需要最小化对解码 mp3 (FFPEG) 的底层 C++ 库的多线程访问,因为它在多线程访问方面存在一些错误。你如何解决它取决于你,我最终将格式更改为 .ogg,当涉及到 mp4 时,我只是使用了谷歌的 exoplayer
【解决方案2】:

我总是用 Singleton Pattern 的修改版本做类似的事情。由于 Android 中到处都需要上下文,因此我将上下文传递给实例:

public class Asset{
     public static Asset(Context context);
}

你还可以在不同的上下文范围内有不同的单例,在这个实现中,例如:

private static Hashtable<Context, Asset> instances;

public static Asset(Context context){
    if (!instances.containKey(context)){
        instances.put(context, new Asset(context));

    return instances.get(context);
}

与经典单例相比,它的优点是您可以定义单例的范围。有时您只需要将实例留在同一个 Activity 中,但第二个 Activity 可能有不同的实例。如果你需要跨不同的Activity,你只需要传递context.getApplicationContext()。

【讨论】:

    【解决方案3】:

    您应该考虑 Singleton pattern。创建一个 MyPlayer 类,它有一个静态方法 getMediaPlayer(),每次调用都会返回相同的 MediaPlayer 实例。

    【讨论】:

    • 你能详细说明一下吗?
    • here 是一个带有服务类服务代码的答案,它可以帮助您在多个活动和片段中使用 mediaPlayer
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    相关资源
    最近更新 更多