在非 activity class 中获取原始资源(声音)

Getting raw resource (sound) in non-activity class

我正在尝试创建一个单例 class 来负责播放游戏声音。我用方法 playSound() 创建了一个单例 class GameSounds。在 res 文件夹中,我有一个带有文件 letter_found.mp3.

的子文件夹 'raw'

这是GameSounds的源代码 class 我写的:

import android.app.Application;
import android.content.Context;
import android.media.MediaPlayer;

public class GameSounds extends Application {

    private static GameSounds gameSounds = new GameSounds();
    private static MediaPlayer soundPlayer;
    private static Context mContext;
    private static int mySoundId = R.raw.letter_found;

    private GameSounds() {
        mContext = this;
    }

    public static GameSounds getInstance() {
        return gameSounds;
    }

    public static void playSound() {
        soundPlayer = MediaPlayer.create(mContext, mySoundId);
        soundPlayer.start();
    }
}

这似乎不起作用,因为我收到以下错误消息:

"java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference"

我不明白为什么会这样。我尝试搜索 Whosebug 但找不到解决方案。

非常感谢help/explanation。

你不应该继承Applicationclass除非你尝试使用单例模式。因为应用程序是基础 class,它包含所有其他组件,例如活动和服务。

相反,GameSound class 应该包含 Context 对象和适当的构造函数。

示例)

public class GameSounds {

    private GameSounds gameSounds;
    private MediaPlayer soundPlayer;
    private WeakReference<Context> mContext;
    private int mySoundId = R.raw.letter_found;

    private GameSounds(Context context) {
        mContext = new WeakReference<>(context);
    }

    public GameSounds getInstance(Context context) {
        if (gameSounds == null) {
            gameSounds = new GameSounds(context);
        }

        return gameSounds;
    }

    public void playSound() {
        soundPlayer = MediaPlayer.create(mContext.get(), mySoundId);
        soundPlayer.start();
    }
}

在这段代码中,有WeakReference<Context>而不是上下文。 WeakReference 用于防止内存泄漏,因为如果您在activity.

之外有一个实例,就会发生内存泄漏。

要播放声音,执行GameSounds.getInstance(this).playSound();即可。

如果尝试播放声音时无法提供上下文,实现initialize方法并在应用程序中调用class就可以了。

public class GameSounds {

    private static GameSounds gameSounds;
    private MediaPlayer soundPlayer;
    private WeakReference<Context> mContext;
    private int mySoundId = R.raw.letter_found;

    private GameSounds(Application context) {
        mContext = new WeakReference<>(context);
    }

    public static void initialize(Application context) {
        if (gameSounds == null) {
            gameSounds = new GameSounds(context);
        }
    }

    public static GameSounds getInstance() {
        if (gameSounds == null) {
            throw new NullPointerException("You need to initialize this code by GameSound.initialize(this) in application class");
        }

        return gameSounds;
    }

    public void playSound() {
        soundPlayer = MediaPlayer.create(mContext.get(), mySoundId);
        soundPlayer.start();
    }
}

在这种情况下,您应该在 class.

中通过 GameSound.initialize(this) 创建 Application class 并初始化 GameSound class

要播放声音,GameSound.getInstance().playSound()即可。

您可以让一个单例持有一个应用程序 Context(不是 Activity 上下文),但实际上您必须在使用单例之前设置此上下文,这可以通过抛出异常来强制执行。请参见下面的示例代码。

public class GameSounds {
    private static Context sContext;

    public static void setContext(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("context cannot be null!");
        }

        // In order to avoid memory leak, you should use application context rather than the `activiy`
        context = context.getApplicationContext();
        if (context == null) {
            throw new IllegalArgumentException("context cannot be null!");
        }

        sContext = context;
    }

    private static Context getContext() {
        if (sContext != null) {
            return (Context)sContext;
        }
        throw new IllegalStateException("sContext was not set yet! Please call method setContext(Context context) first.");
    }

    // the rest of other methods. e.g. playSounds()
    private static GameSounds gameSounds = new GameSounds();
    private GameSounds() {

    }

    public static GameSounds getInstance() {
        return gameSounds;
    }


    public void playSound() {

        Context context = getContext();

        soundPlayer = MediaPlayer.create(context, mySoundId);
        soundPlayer.start();
    }
}