我正在Android Studio中编写一个应用程序,几乎每个活动都会以一种或另一种方式播放1-3秒的声音。活动将包含方形图像,触摸时会发出声音。
我在所有活动中一遍又一遍地编写了用于媒体播放的代码,因此我认为可以制作一个单独的类来处理播放。另外,我在不同活动的声音互相播放时遇到问题:
public class SoundPlayback {
/** provides access to volume and ringer mode control. */
private static AudioManager audioManager;
/** Handles playback of all the sound files */
// Initialize the MediaPlayer as a global variable
private static MediaPlayer mediaPlayer;
/**
* The listener gets triggered when the mediaPlayer has completed
* playing the audio file.
*/
private static MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
releaseMediaPlayer();
}
};
/**
* This listener gets triggered whenever the audio focus changes
* (i.e., we gain or lose audio focus because of another app or device).
*/
private static AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// The AUDIOFOCUS_LOSS_TRANSIENT case means that we've lost audio focus for a
// short amount of time. The AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK case means that
// our app is allowed to continue playing sound but at a lower volume. We'll treat
// both cases the same way because our app is playing short sound files.
// Pause playback and reset player to the start of the file. That way, we can
// play the word from the beginning when we resume playback.
mediaPlayer.pause();
mediaPlayer.seekTo(0);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// The AUDIOFOCUS_LOSS case means we've lost audio focus
// Stop playback and clean up resources
releaseMediaPlayer();
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// The AUDIOFOCUS_GAIN case means we have regained focus and can resume playback.
mediaPlayer.start();
}
}
};
public static void initializeManagerService(Context context) {
// Create and setup the Audio Manager to request audio focus
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
public static void initializePlayer(Context context, int audioResourceID) {
// Request audio focus in order to play the audio file. The app needs to play a
// short audio file, so we will request audio focus with a short amount of time
// with AUDIOFOCUS_GAIN_TRANSIENT.
int result = audioManager.requestAudioFocus(audioFocusChangeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// Using the factory method create() we set the audio resource to be played when
// chosen category is selected.
mediaPlayer = MediaPlayer.create(context, audioResourceID);
// Stat playback of audio
mediaPlayer.start();
// Listen for when the playback has finished
mediaPlayer.setOnCompletionListener(completionListener);
}
}
/** Clean up the media player by releasing its resources */
public static void releaseMediaPlayer() {
// If the media player is not null, then it may be currently playing a sound.
if (mediaPlayer != null) {
// Regardless of the current state of the media player, release its resources
// because we no longer need it.
mediaPlayer.release();
// Set the media player back to null. For our code, we've decided that
// setting the media player to null is an easy way to tell that the media player
// is not configured to play an audio file at the moment.
mediaPlayer = null;
// Regardless of whether or not we were granted audio focus, abandon it. This also
// unregisters the AudioFocusChangeListener so we don't get anymore callbacks.
audioManager.abandonAudioFocus(audioFocusChangeListener);
}
}
}
并在活动的onCreate方法中这样调用:
// Store context to be used for calling SoundPlayback
final Context context = getApplicationContext();
// Initialize the Audio Manager AUDIO_SERVICE
SoundPlayback.initializeManagerService(context);
// Set onItemClickListener to handle opening of categories
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// Store the current Category object that the CategoryAdapter is iterating, using
// the ArrayList method get(). Input parameter is int index, which corresponds
// to the given position the user clicked on.
Category currentCategory = categories.get(position);
// Release the media player if it currently exists, because we are about to
// play a different sound file.
SoundPlayback.releaseMediaPlayer();
// Initialize playback of the sound related to the item the user has selected
SoundPlayback.initializePlayer(context, currentCategory.getAudioResourceID());
});
此解决方案非常适合我的应用程序,但感觉就像我在作弊。使用仅包含静态字段和方法的类是正确的情况吗?我应该改用单例吗?
我不理会服务,因为播放的声音只有几秒钟长,而且感觉不需要所需的内存/电池使用量。
答案 0 :(得分:1)
有可能,但单例总是更好的解决方案。除非您必须使用全静态解决方案,否则很难理解这一点,因此请尝试一下,然后看它对您的影响。
例如,您如何为此编写单元测试?
再举一个例子-假设您有一个Notification界面并且有许多实现...声音通知程序,阴影通知程序,手表通知程序,电子邮件通知程序... ...您如何调整静态实现并使用它在带有其他“通知”对象的集合中?并不是您现在想要的,只是使用所有静态方法非常不灵活,使得解决许多问题的解决方案涉及傻适配器和复制/粘贴解决方案。
顺便说一句,单例也不是一个好主意,但是如果您不使用DI,它可能是您可以使用的最佳简便选择。