Random NullPointerException in SoundManager

I use this standard SoundManager. It works fine on all my devices, but only now on the market, and then I get these errors.

  • NullPointerException in SoundManager.playSound (SoundManager.java:87)

  • NullPointerException in SoundManager.cleanup (SoundManager.java:107)

Here is the code:

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

This is a cleanup call that is in initial activity:

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

Does anyone know what can cause these rare rare errors and how to prevent them? This happens in all of my applications that use this SoundManager ... Even a little spec can help.

+6
source share
2 answers

When you initialize your SoundManager, use the Application Context. You may have problems between actions. If SoundManager is more time than your activity. You can even initialize in your application.

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

I also agree with WarrenFaith. The only statics should be _instance and getInstance ().

Also, if you download your sounds in the application class, you do not need to worry about synchronization.

If this helps you see the code I'm using. It uses the OpenSL SoundPool library from http://code.google.com/p/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() ; } } 

Work continues, but work is being done.

+3
source

A bit mixed up. You do not and should not use the Singleton template with static methods and variables (except for the variables getInstance () and mInstance). It does not make sense.

You must get rid of the statics and make full use of the class as a singleton to make sure that no variables can be empty due to concurrency problems (I think your null problem is the result of concurrency)

Here is the class I would use:

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

Usage is now a little longer, but should remove random NPEs. You must call this in your Application class inside onCreate() .

 SoundManager.getInstance().initSounds(context); 

Then wherever you need to use the class:

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

Update:

To respond to your comment:

If you create an instance in Application :: onCreate (), you will always have an instance around and with the instance there will also be an internal variable. If a user leaves the application, two cases may occur:

  • it can be destroyed, but after the user starts the application again, it will be called again when it appears again.
  • nothing happens and the instance still exists.

So, in both cases, you will never lose an instance.

Just because others can do it in a certain way, it does not make the right path.

+3
source

Source: https://habr.com/ru/post/922645/


All Articles