2013-02-08 51 views
3

移动在我下面的教程来的Soundpool融入我的应用程序之前加载,这是在本教程中给出的代码:等待的Soundpool与应用

package com.example.soundpoolexample; 

import android.app.Activity; 
import android.media.AudioManager; 
import android.media.SoundPool; 
import android.media.SoundPool.OnLoadCompleteListener; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 

public class SoundPoolExample extends Activity implements OnTouchListener { 
    private SoundPool soundPool; 
    private int soundID; 
    boolean loaded = false; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     View view = findViewById(R.id.textView1); 
     view.setOnTouchListener(this); 
     // Set the hardware buttons to control the music 
     this.setVolumeControlStream(AudioManager.STREAM_MUSIC); 
     // Load the sound 
     soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0); 
     soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() { 
      @Override 
      public void onLoadComplete(SoundPool soundPool, int sampleId, 
        int status) { 
       loaded = true; 
      } 
     }); 
     soundID = soundPool.load(this, R.raw.sound1, 1); 

    } 

    @Override 
    public boolean onTouch(View v, MotionEvent event) { 
     if (event.getAction() == MotionEvent.ACTION_DOWN) { 
      // Getting the user sound settings 
      AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); 
      float actualVolume = (float) audioManager 
        .getStreamVolume(AudioManager.STREAM_MUSIC); 
      float maxVolume = (float) audioManager 
        .getStreamMaxVolume(AudioManager.STREAM_MUSIC); 
      float volume = actualVolume/maxVolume; 
      // Is the sound loaded already? 
      if (loaded) { 
       soundPool.play(soundID, volume, volume, 1, 0, 1f); 
       Log.e("Test", "Played sound"); 
      } 
     } 
     return false; 
    } 
} 

的问题是,我想以确保应用程序只在SoundPool加载成功后才前进,并且不必在每次播放声音之前检查它是否已加载。我将如何做到这一点?

我相信这是一个残暴的解决方案,但将这样的事情,下班后我加载的onCreate声音文件?:

... 

    soundID = soundPool.load(this, R.raw.sound1, 1); 
    while (!loaded) {} 

... 

我肯定有一个更好的办法。

回答

1

您可以使用Semaphore一个优雅的解决方案,但是尽量避免阻塞onCreate(),因为应用程序不会响应,这里的代码只是为了说明使用Semaphore

public class SoundPoolExample extends Activity implements OnTouchListener { 

public static final Semaphore semaphore = new Semaphore(0); 

/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() { 
     @Override 
     public void onLoadComplete(SoundPool soundPool, int sampleId, 
       int status) { 

      semaphore.release(); 
     } 
    }); 
    soundID = soundPool.load(this, R.raw.sound1, 1); 

    // Will block since there is not permits (the semaphore is initialized with 0) 
    // then will continue once semaphore.release(); is called i.e. after loading is finished 
    semaphore.acquire(); 
} 

// rest of your class 

} 
+0

谢谢,看着它。这是所有其他应用程序等待其资源加载的方式吗?我的意思是,当你打开任何应用程序,有一个加载屏幕。那么他们在后台运行信号量还是其他的东西? – capcom 2013-02-08 22:56:31

+0

绝对有些应用程序正在使用'Semaphore'来做,你也可以用'wait/notify'来做到这一点,如果你想等待最长的时间,这是有帮助的。 – iTech 2013-02-08 22:59:51

+0

当SoundPool(专为它设计)使用几种声音时,我相信CountDownLatch比Semaphore更好。 – personne3000 2014-07-13 07:30:55

0

我试着iTech的信号量解决方案并没有奏效。该应用程序坐在那里等待永远。

onCreate与主线程一起被调用。此线程创建声音池,开始加载声音,然后停放等待加载完成。似乎它应该工作,对吧?

经过一番调查后,我发现SoundPool具有始终在使用Looper/Handler机制的主线程上运行onLoadComplete的未记录行为。由于主线程暂停,它不能接收该事件,并且应用程序挂起。

我们可以看到这是如何工作在Android源代码SoundPool.java:

public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) 
{ 
    synchronized(mLock) { 
     if (listener != null) { 
      // setup message handler 
      Looper looper; 
      if ((looper = Looper.myLooper()) != null) { 
       mEventHandler = new EventHandler(mProxy, looper); 
      } else if ((looper = Looper.getMainLooper()) != null) { 
       mEventHandler = new EventHandler(mProxy, looper); 
      } else { 
       mEventHandler = null; 
      } 
     } else { 
      mEventHandler = null; 
     } 
     mOnLoadCompleteListener = listener; 
    } 
} 

这表明,它在默认情况下使用主线程的Looper除非你有自己的一套了一个活套。

最简单的解决方案是在新线程中运行整个声音加载过程,并立即从主线程返回。如有必要,您可以使用主线程显示加载屏幕。加载声音时,主线程可以自由接收onLoadComplete调用并发出声音加载线程的信号。声音加载线程可以通过信号等待该信号,或等待/通知共享对象。

0

解决这个问题有点棘手。我以下面的方式处理类似的情况。在结构方面,它看起来像这样:

  1. handler在主线程上被实例化。
  2. handleMessage被实施,其负责所有加载的声音样本的自定义通知消息。
  3. A SoundPool在主线程中被实例化。
  4. 新线程启动其中:

    4.1设置SoundPool.OnLoadCompleteListener这将使用从主线程中实例化处理程序自定义通知消息。

    4.2利用实例化的Soundpool发出的声音样本的异步加载(SoundPool类的load方法)

然后,有保持声音样本装载的状态的变量。它可以有三种状态:无法加载,加载,加载

该变量的状态设置为加载在上述第四步的线程中。

当自定义通知消息到达时,此变量的状态设置为加载handleMessage中。

在播放任何声音之前检查此状态变量。如果尚未加载声音样本,则会显示半全屏对话框,指出声音样本正在加载并在加载完成时自动消失。该对话不能被用户解雇。这是通过调用该对话框的dismiss方法编程从handleMessage方法,如果显示。该对话框有一个很好的进度条动画。这是特定于我的应用程序,并可以为其他应用程序做不同的处理。

你可能会问为什么这么做很复杂。问题是整个Android GUI API是一个事件机器,根据定义,您在主线程上运行的代码不应该执行任何耗时的过程,因为这会导致整个GUI无法响应。实际上,当Android检测到这种情况时,它会将它们记录到logcat中。例如,如果我使用了信号量,则不会有进度条动画,因为主要的GUI线程会被阻塞。使用信号量这种方式只是表现出不了解Android GUI架构的事实,并且如果发布信号量的某个部分出现问题,它可能很容易导致应用程序挂起。

希望这会有所帮助。

0

我认为同时使用的Soundpool,每个人都应该注意两种情况:

  1. 的Soundpool已经废弃了API + 21,而不是你应该使用SoundPool.Builder
  2. 如果您的Soundpool在主线程中运行,采取它到另一个线程,因为它阻止你的用户界面