SoundManager中偶尔出现NullPointerException

时间:2012-08-11 20:55:46

标签: android

我使用这个标准的SoundManager。它在我的所有设备上运行良好,但现在只在市场上出现,然后我得到这些错误

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

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

  3. 以下是代码:

    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的所有应用程序中...即使是一点点的推测也可能有所帮助。

2 个答案:

答案 0 :(得分:3)

有点混淆。您没有(也不应该)使用具有静态方法和变量的Singleton模式(getInstance()和mInstance变量除外)。这没有意义。

你应该摆脱静态并完全使用类作为单例,以确保没有变量因为并发问题而可能为null(我猜你的null问题是并发的结果)

以下是我将使用的课程:

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

中的Application类中调用它
SoundManager.getInstance().initSounds(context);

然后,无论你需要使用哪个类:

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

更新

回答你的评论:

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

  1. 它可以被销毁但是一旦用户再次进入应用程序就会再次调用onCreate
  2. 没有任何反应,实例仍在那里。
  3. 因此,在这两种情况下,您都不会丢失实例。

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

答案 1 :(得分: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() ;
    }


}

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