6

我使用这个标准的 SoundManager。它在我的所有设备上都可以正常工作,但仅在市场上偶尔出现这些错误

  1. SoundManager.playSound(SoundManager.java:87) 中的 NullPointerException

  2. SoundManager.cleanup(SoundManager.java:107) 中的 NullPointerException

这是代码:

public class SoundManager {

    private static SoundManager _instance;
    private static SoundPool mSoundPool; 
    private static HashMap<Integer, Integer> mSoundPoolMap; 
    private static AudioManager  mAudioManager;
    private static Context mContext;

    private SoundManager(){   }

    static synchronized public SoundManager getInstance(){
        if (_instance == null) 
          _instance = new SoundManager();
        return _instance;
     }


    public static  void initSounds(Context theContext){ 
         mContext = theContext;
         mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
         mSoundPoolMap = new HashMap<Integer, Integer>(); 
         mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);        
    } 


    public static void addSound(int Index,int SoundID){
        mSoundPoolMap.put(Index, mSoundPool.load(mContext, SoundID, 1));
    }


    public static void loadSounds(){

        mSoundPoolMap.put(1, mSoundPool.load(mContext, R.raw.kick1, 1)); 
        mSoundPoolMap.put(2, mSoundPool.load(mContext, R.raw.kick2, 1)); 
        mSoundPoolMap.put(3, mSoundPool.load(mContext, R.raw.kick3, 1));    


    }


    public static void playSound(int index, float volume){      
             **line 87:** float streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
             streamVolume = streamVolume / mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
             mSoundPool.play(mSoundPoolMap.get(index), streamVolume*volume, streamVolume*volume, 1, 0, 1); 
    }


    public static void stopSound(int index){
        mSoundPool.stop(mSoundPoolMap.get(index));
    }

    public static void cleanup(){
        **line 107:** mSoundPool.release();
        mSoundPool = null;
        mSoundPoolMap.clear();
        mAudioManager.unloadSoundEffects();
        _instance = null;

    }
}

这是启动活动中的清理调用:

    //REMOVE SOUND MEMORY ALLOCATION
    @Override
    public void onDestroy()
        {
            super.onDestroy();
            SoundManager.cleanup();
        }

有谁知道是什么导致了这些偶尔出现的罕见错误以及如何防止它们?这发生在我所有使用此 SoundManager 的应用程序中......即使有点奇怪的猜测也会有所帮助。

4

2 回答 2

3

有一点混乱。您不(也不应该)将单例模式与静态方法和变量(getInstance() 和 mInstance 变量除外)一起使用。这没有意义。

您应该摆脱静态并将该类完全用作单例,以确保由于并发问题没有变量可能为空(我猜您的空问题是并发的结果)

这是我将使用的类:

public class SoundManager {
    // syncronized creation of mInstance
    private final static SoundManager mInstance = new SoundManager(); 
    private SoundPool mSoundPool; 
    private HashMap<Integer, Integer> mSoundPoolMap; 
    private AudioManager  mAudioManager;
    private Context mContext;

    private SoundManager() {}

    public static SoundManager getInstance() {
        return _instance;
    }

    public void initSounds(Context theContext) { 
        mContext = theContext;
        mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
        mSoundPoolMap = new HashMap<Integer, Integer>(); 
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);        
    } 

    public void addSound(int Index,int SoundID){
        mSoundPoolMap.put(Index, mSoundPool.load(mContext, SoundID, 1));
    }

    public void loadSounds() {
        mSoundPoolMap.put(1, mSoundPool.load(mContext, R.raw.kick1, 1)); 
        mSoundPoolMap.put(2, mSoundPool.load(mContext, R.raw.kick2, 1)); 
        mSoundPoolMap.put(3, mSoundPool.load(mContext, R.raw.kick3, 1));
    }

    public void playSound(int index, float volume){      
        float streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
        streamVolume = streamVolume / mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        mSoundPool.play(mSoundPoolMap.get(index), streamVolume*volume, streamVolume*volume, 1, 0, 1); 
    }

    public void stopSound(int index) {
        mSoundPool.stop(mSoundPoolMap.get(index));
    }

    // I wouldn't use this until I am extremely sure that I
    // will never ever use the SoundManager again... so
    // probably never. Let the SoundManager die when the application dies...
    public void cleanup() {
        mSoundPool.release();
        mSoundPool = null;
        mSoundPoolMap.clear();
        mAudioManager.unloadSoundEffects();
    }
}

现在的使用时间有点长,但应该删除随机 NPE。您应该在onCreate().

SoundManager.getInstance().initSounds(context);

然后在需要使用该类的任何地方:

SoundManager.getInstance().playSound(index, volume);
// or what ever you need

更新:

要回答您的评论:

如果您在 Application::onCreate() 中创建实例,您将始终拥有该实例,并且该实例也是内部变量。当用户离开应用程序时可能会发生两种情况:

  1. 它可以被销毁,但是一旦用户再次进入应用程序,就会再次调用 onCreate
  2. 什么都没有发生,实例仍然存在。

因此,在这两种情况下,您都不会丢失实例。

仅仅因为其他人可能会以特定的方式这样做并不能使这种方式成为正确的方式。

于 2012-08-15T10:15:33.183 回答
3

当您初始化 SoundManager 时,请使用应用程序上下文。您可能在活动之间遇到问题。如果 SoundManager 的寿命比您的活动长。您甚至可以在您的应用程序中进行初始化。

public class MyAndroidApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        SoundManager.initSounds(this);
    }
}

我也同意 WarrenFaith 的观点。唯一的静态应该是 _instance 和 getInstance()。

此外,如果您在 Application 类中加载声音,则无需担心同步问题。

如果有帮助,您可以查看我使用的代码。它利用来自http://code.google.com/p/opensl-soundpool/的 OpenSL SoundPool 库

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;

import com.kytomaki.openslsoundpool.JavaSoundPool;
import com.kytomaki.openslsoundpool.OpenSLSoundPool;
import com.kytomaki.openslsoundpool.SoundPoolIf;

final public class SoundManager
{
    // Predetermined sound ID's
    public static final int             NO_SOUND        = -1 ;
    public static final int             WINNER          = -2 ;

    // Tag for logging
    protected static final String       TAG             = "SoundManager" ;

    /** Used to load and play sounds **/
    private Context                     context ;

    /** Sound can be disable from separate thread **/
    private final AtomicBoolean         useSound ;

    // Sound Arrays
    private final ArrayList<Integer>    winningSounds ;
    private final SoundPoolIf           soundPool ;
    private final HashMap<Integer, Integer> soundPoolMap ;
    private final AudioManager          audioManager ;

    /** Singleton object for sound play back **/
    private static SoundManager         soundManagerInstance ;


    private static final int            USE_SOUNDPOOL   = 1 ;
    private static final int            USE_OPENSL      = 2 ;
    private static int                  use             = USE_SOUNDPOOL ;



    /**
     * Private Method to create a new SoundManager<br>
     * This is a Singleton Object
     * @param context Should be the Application Context
     */
    private SoundManager( final Context context )
    {
        setContext( context ) ;
        useSound = new AtomicBoolean( true ) ;
        audioManager = (AudioManager) context.getSystemService( Context.AUDIO_SERVICE ) ;

        soundPoolMap = new HashMap<Integer, Integer>() ;
        winningSounds = new ArrayList<Integer>() ;

        if ( use == USE_OPENSL )
        {
            soundPool = new OpenSLSoundPool( 2, OpenSLSoundPool.RATE_44_1, OpenSLSoundPool.FORMAT_16, 1) ;
        } else {
            soundPool = new JavaSoundPool( 2 ) ;
        }
    }

    /**
     * Must be called before using<br>
     * Best to initialize in Application Class
     * @param context
     */
    public static void initSoundManager( final Context context )
    {
        if ( soundManagerInstance == null )
        {
            soundManagerInstance = new SoundManager( context ) ;
        }
        else
        {
            throw new UnsupportedOperationException( "Sound manager has already been created" ) ;
        }
    }

    /**
     * Overloaded method to allow use of OpenSL
     * @param context
     * @param useOpenSL
     */
    public static void initSoundManager( final Context context, final boolean useOpenSL){
        if(useOpenSL){
            use = USE_OPENSL;
        }
        initSoundManager(context);
    }

    /**
     * Must initialize first with {@link SoundManager#initSoundManager(Context)}
     * @return instance of SoundManager
     */
    public static SoundManager getSoundManagerInstance()
    {
        if ( soundManagerInstance != null )
        {
            return soundManagerInstance ;
        }
        else
        {
            throw new UnsupportedOperationException( "SoundManager must be initalized" ) ;
        }
    }


    /**
     * Add a sound from an android resource file R.id.sound<br>
     * To be played back with SoundManager.play(soundId)
     * @param soundId
     * @param soundResourceId
     */
    public void addSound( final int soundId, final int soundResourceId )
    {
        soundPoolMap.put(soundId, soundPool.load(getContext(), soundResourceId));
    }

    /**
     * Adds a winning sound from a resource to be played at random<br>
     * Called by SoundManager.play(WINNER)
     * @param soundResourceId
     */
    public void addWinningSound( final int soundResourceId )
    {
        winningSounds.add( soundResourceId ) ;
    }

    /**
     * Plays a sound first checking if sound is enabled
     * @param soundToPlay soundId or WINNER to play random winning sound
     */
    public synchronized void play( final int soundToPlay )
    {
        if ( isUseSound() )
        {
            switch ( soundToPlay )
            {
                case NO_SOUND :
                    break ;
                case WINNER :
                    // Play a random winning sound using media player
                    final MediaPlayer mp ;
                    mp = MediaPlayer.create( getContext(), randomWinnerSound() ) ; 
                    if ( mp != null )
                    {
                        mp.seekTo( 0 ) ;
                        mp.start() ;
                    }
                    break ;
                default :
                    playSound( soundToPlay ) ;
                    break ;
            }
        }
    }

    /**
     * Calls soundpool.play
     * @param soundToPlay
     */
    private void playSound( final int soundToPlay )
    {
        float streamVolume = audioManager.getStreamVolume( AudioManager.STREAM_MUSIC ) ;
        streamVolume = streamVolume / audioManager.getStreamMaxVolume( AudioManager.STREAM_MUSIC ) ;
        soundPool.play(soundPoolMap.get(soundToPlay), streamVolume);
    }

    /**
     * @return random winning sound position
     */
    private int randomWinnerSound()
    {
        final Random rand = new Random() ;
        final int playNumber = rand.nextInt( winningSounds.size() ) ;
        return winningSounds.get( playNumber ) ;
    }

    /**
     * @param context the context to set
     */
    private final void setContext( final Context context )
    {
        this.context = context ;
    }

    /**
     * @return the context
     */
    private final Context getContext()
    {
        return context ;
    }

    /**
     * @param useSound false to disable sound
     */
    public final void setUseSound( final boolean useSound )
    {
        this.useSound.set( useSound ) ;
    }

    /**
     * @return the useSound
     */
    public boolean isUseSound()
    {
        return useSound.get() ;
    }


}

仍在进行中的工作,但完成了工作。

于 2012-08-15T23:42:57.630 回答