Android SoundManager中偶尔出现NullPointerException
我使用这个标准的SoundManager。它在我所有的设备上都能正常工作,但只是在市场上偶尔出现这些错误Android SoundManager中偶尔出现NullPointerException,android,Android,我使用这个标准的SoundManager。它在我所有的设备上都能正常工作,但只是在市场上偶尔出现这些错误 SoundManager.playSound中的NullPointerException(SoundManager.java:87) SoundManager.cleanup(SoundManager.java:107)中的NullPointerException 代码如下: public class SoundManager { private static SoundManag
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;
}
}
有人知道是什么导致了这些偶尔出现的罕见错误,以及如何预防它们吗?这发生在我所有使用此SoundManager的应用程序中。。。即使是一点od推测也会有所帮助。有点混乱。您不(也不应该)对静态方法和变量使用单例模式(getInstance()和minInstance变量除外)。这没有道理 您应该摆脱静态,并将该类完全作为一个单例使用,以确保没有任何变量可能因为并发问题而为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();
}
}
然后,在需要使用该类的任何地方:
SoundManager.getInstance().playSound(index, volume);
// or what ever you need
更新:
要回答您的评论:
如果您在Application::onCreate()中创建实例,那么您将始终拥有该实例,并且该实例也是内部变量。当用户离开应用程序时,可能会发生两种情况:
仅仅因为其他人可能会以特定的方式执行此操作,并不意味着此方法是正确的。当您初始化SoundManager时,请使用应用程序上下文。您可能会在活动之间移动问题。如果SoundManager比您的活动寿命长。您甚至可以在应用程序中初始化
public class MyAndroidApp extends Application {
@Override
public void onCreate() {
super.onCreate();
SoundManager.initSounds(this);
}
}
我也同意沃伦信仰。唯一的静态应该是_instance和getInstance()
此外,如果在应用程序类中加载声音,则无需担心同步问题
如果有帮助的话,你可以看看我使用的代码。它利用了
import java.util.ArrayList;
导入java.util.HashMap;
导入java.util.Random;
导入java.util.concurrent.AtomicBoolean;
导入android.content.Context;
导入android.media.AudioManager;
导入android.media.MediaPlayer;
导入com.kytomaki.openslsoundpool.JavaSoundPool;
导入com.kytomaki.openslsoundpool.openslsoundpool;
导入com.kytomaki.openslsoundpool.SoundPoolIf;
最终公共类SoundManager
{
//预定的声音ID
公共静态最终整数无音=-1;
公共静态最终整数获胜者=-2;
//日志记录标签
受保护的静态最终字符串标记=“SoundManager”;
/**用于加载和播放声音**/
私人语境;
/**声音可以从单独的线程禁用**/
私人最终原子声;
//声音阵列
私人最终ArrayList winningSounds;
私人最终声音池IF声音池;
私有最终HashMap soundPoolMap;
私人最终音频经理AudioManager;
/**用于声音播放的单例对象**/
私有静态SoundManager soundManagerInstance;
私有静态最终int使用_SOUNDPOOL=1;
私有静态最终整数使用_OPENSL=2;
私有静态int use=use_SOUNDPOOL;
/**
*创建新SoundManager的专用方法
*这是一个单例对象
*@param context应该是应用程序上下文
*/
专用SoundManager(最终上下文)
{
setContext(context);
useSound=新原子布尔值(true);
audioManager=(audioManager)context.getSystemService(context.AUDIO\u服务);
soundPoolMap=新HashMap();
winningSounds=新的ArrayList();
if(use==use_OPENSL)
{
soundPool=新的OpenSLSoundPool(2,OpenSLSoundPool.RATE_44_1,OpenSLSoundPool.FORMAT_16,1);
}否则{
soundPool=新的JavaSoundPool(2);
}
}
/**
*必须在使用前调用
*最好在应用程序类中初始化
*@param上下文
*/
公共静态void initSoundManager(最终上下文)
{
如果(soundManagerInstance==null)
{
soundManagerInstance=新的SoundManager(上下文);
}
其他的
{
抛出新的UnsupportedOperationException(“已创建声音管理器”);
}
}
/**
*重载方法以允许使用OpenSL
*@param上下文
*@param useonsl
*/
公共静态void initSoundManager(最终上下文上下文,最终布尔值useOpenSL){
如果(使用OpenSL){
使用=使用OPENSL;
}
initSoundManager(上下文);
}
/**
*必须首先用{@link SoundManager#initSoundManager(Context)}初始化
*@SoundManager的返回实例
*/
公共静态SoundManager getSoundManagerInstance()
{
if(soundManagerInstance!=null)
{
返回音质;
}
其他的
{
抛出新的UnsupportedOperationException(“SoundManager必须初始化”);
}
}
/**
*从android资源文件R.id.sound添加声音
*使用SoundManager.play(soundId)播放
*@param soundId
*@param soundResourceId
*/
public void addSound(最终int-soundId,最终int-soundResourceId)
{
soundPoolMap.put(soundId,soundPool.load(getContext(),soundResourceId));
}
/**
*从要随机播放的资源中添加获胜的声音
*由SoundManager调用。播放(赢家)
*@param soundResourceId
*/
聚氨基甲酸酯
SoundManager.getInstance().playSound(index, volume);
// or what ever you need
public class MyAndroidApp extends Application {
@Override
public void onCreate() {
super.onCreate();
SoundManager.initSounds(this);
}
}
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() ;
}
}