6

私はこの標準のSoundManagerを使用しています。私のすべてのデバイスで問題なく動作しますが、市場では今だけこれらのエラーが発生します

  1. SoundManager.playSound(SoundManager.java:87) の NullPointerException

  2. SoundManager.cleanup の NullPointerException(SoundManager.java:107)

コードは次のとおりです。

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 変数を除く) で Singleton パターンを使用しないでください (使用すべきではありません)。これは意味がありません。

静的を取り除き、クラスを完全にシングルトンとして使用して、同時実行の問題のために変数が 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 を削除する必要があります。内の Application クラスでこれを呼び出す必要がありますonCreate()

SoundManager.getInstance().initSounds(context);

次に、クラスを使用する必要がある場所:

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

アップデート:

あなたのコメントに答えるには:

Application::onCreate() でインスタンスを作成すると、常にインスタンスが存在し、インスタンスとともに内部変数も保持されます。ユーザーがアプリを終了すると、次の 2 つのケースが発生する可能性があります。

  1. 破棄することはできますが、ユーザーがアプリに再び入るとすぐに onCreate が再度呼び出されます
  2. 何も起こらず、インスタンスはまだそこにあります。

したがって、どちらの場合でも、インスタンスが失われることはありません。

他の人が特定の方法でそれを行う可能性があるからといって、その方法が正しいとは限りません。

于 2012-08-15T10:15:33.183 に答える
3

SoundManager を初期化するときは、Application Context を使用します。アクティビティ間で問題が移動する可能性があります。SoundManager があなたのアクティビティよりも長生きしている場合。アプリケーションで初期化することもできます。

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

私もウォーレンフェイスに同意します。static は _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 に答える