简体   繁体   English

在非活动类中获取原始资源(声音)

[英]Getting raw resource (sound) in non-activity class

I'm trying to create a singleton class that will be responsible for playing game sounds. 我正在尝试创建一个负责播放游戏声音的单例类。 I created a singleton class GameSounds with a method playSound() . 我使用playSound()方法创建了一个单例类GameSounds In the res folder I have aa subfolder 'raw' with a file letter_found.mp3 . 在res文件夹中,我有一个子文件夹'raw',其文件为letter_found.mp3

This is the source code of the GameSounds class I wrote: 这是我编写的GameSounds类的源代码:

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();
    }
}

This doesn't seem to work as I'm getting the following error message: 当我收到以下错误消息时,这似乎不起作用:

"java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference" “ java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法'android.content.res.Resources android.content.Context.getResources()'”

I don't understand why this is happening. 我不明白为什么会这样。 I tried to search Stackoverflow but couldn't find a solution. 我试图搜索Stackoverflow,但找不到解决方案。

Any help/explanation is greatly appreciated. 任何帮助/说明,我们将不胜感激。

You shouldn't inherit Application class unless you try to use Singleton pattern. 除非尝试使用Singleton模式,否则不应继承Application Because Application is base class which contains all other components such as activities and services. 因为Application是基类,所以它包含所有其他组件,例如活动和服务。

Instead, GameSound class should contain Context object and proper constructor. 相反,GameSound类应包含Context对象和适当的构造函数。

Example) 例)

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();
    }
}

In this code, there is WeakReference<Context> instead of Context. 在此代码中,有WeakReference<Context>而不是Context。 WeakReference is used to prevent memory leaks because memory leaks can occur if you have an instance outside the activity. WeakReference用于防止内存泄漏,因为如果活动之外有一个实例,则可能发生内存泄漏。

To play sound, execute GameSounds.getInstance(this).playSound(); 要播放声音,请执行GameSounds.getInstance(this).playSound(); is fine. 很好

If Context can't provide when try to play sound, implement initialize methods and called in Application class can be ok. 如果尝试播放声音时Context无法提供,则可以实现initialize方法并在Application类中调用可以。

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();
    }
}

In this case, you should make Application class and initialize GameSound class by GameSound.initialize(this) in Application class. 在这种情况下,您应该创建Application类,并通过Application类中的GameSound.initialize(this)初始化GameSound类。

To play sound, GameSound.getInstance().playSound() is fine. 要播放声音,可以使用GameSound.getInstance().playSound()

You can have a Singleton holding an Application Context (NOT Activity context) but practically you have to set this context before you use your singleton which can be enforced by throwing exceptions. 您可以让一个Singleton持有一个应用程序Context (NOT Activity上下文),但是实际上您必须在使用Singleton之前设置此上下文,该Singleton可以通过引发异常来强制执行。 See below example code. 请参见下面的示例代码。

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();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM