2012-08-11 58 views
6

この標準のSoundManagerを使用します。 SoundManager.playSoundで、それは今だけ私のすべてのデバイスではなく、市場で正常に動作し、その後、私はこれらのエラーを取得しSoundManagerで時折NullPointerExceptionが発生する

  1. のNullPointerException(SoundManager.java:87)SoundManager.cleanupで

  2. 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を使用する私のすべてのアプリケーションで発生します...少しの推測でさえ助けることができます。

+0

エラーを再現できる場合は、 'playSound'メソッドと' cleanup'メソッドの両方で 'mSoundPool'、' mSoundPoolMap'、および 'mAudioManager'の値を' Log'してください。そのうちの1つはnullにバインドされています。しかし、initSoundsが呼び出される前に何かが呼び出される状況があると私は推測しています。 – Eric

+0

ありがとう、エリック。私は、私のデバイス上でエラーを再現することができません、そうでなければ、原因を見つけることはずっと簡単です。 – Lumis

+1

行87と107をマークできますか? – WarrenFaith

答えて

3

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

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

また、WarrenFaithに同意します。唯一の統計は_instanceとgetInstance()です。

また、アプリケーションクラスでサウンドを読み込むと、同期について心配する必要はありません。

私が使用しているコードを見れば分かります。それはまだ進行中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() ; 
    } 


} 

仕事からOpenSL SoundPoolライブラリを使用していますが、仕事を取得します。

+1

この良い情報と例をありがとう。 「OpenSlのページでは、「SoundPoolはサムスンギャラクシーS2(おそらく他のデュアルコア端末)でクラッシュしているようだ」と言われています。 – Lumis

+0

OpenSLが通常のSoundPoolよりも待ち時間が短いかどうか知っていますか?私は、Androidの楽器アプリが役に立たない(タッチで遊ぶのが遅すぎる)アプリを見つけました。 – Lumis

+0

SoundPoolの問題は、ほとんどの人が信じるよりはるかに広く普及しています。私はすべてのデュアルコアジンジャーブレッドフォンが影響を受けると思います。はい、OpenSLの待ち時間は何倍も優れています。 – theJosh

3

少し混乱があります。静的メソッドと変数(getInstance()とmInstance変数を除く)を持つシングルトンパターンを使用しないでください(そうすべきではありません)。これは意味をなさない。

あなたが原因並行性の問題の静力学を取り除くと何の変数が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 

更新:

あなたのコメントに答えるために:あなたは、アプリケーション内のインスタンスを作成する場合

を::のonCreate( )常ににインスタンスがあり、内部変数もインスタンスになります。 2つのケースは、ユーザーがアプリを離れるときに起こるかもしれない:それは破壊される可能性が

  1. をしかし、ユーザーが再び
  2. 何も起こらないアプリに入り、インスタンスがまだあるとのonCreateよりも、すぐに再び呼び出されます。

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

他の人が特定の方法で行う可能性があるため、正しい方法ではありません。

+0

私は、サウンドチュートリアルでおそらくあなたのウェブサイトからこれを選んだと思うが、私は今変更されて見ることができます。多くの人が、StackOverwlowで見ることができる静的変数を持つSound Managerを使用しているようです。静的変数を持つポイントは、誰かが離れてアプリケーションに戻ったときに値を保持することができるということです。そのため、これはインスタンス自体を含むSound Managerにとっては良いことではないでしょう... – Lumis

+0

私の答えはいくつかの説明で更新されました – WarrenFaith

+0

私はあなたのアプリケーションが破壊されたときにプロセスがkillされないかもしれないので、静的変数を初期化したまま読み込み、メモリリークを引き起こす可能性があります。 – Jochem

関連する問題