Create SoundPool
BeatBox.java
public class BeatBox { //... private static final int MAX_SOUNDS = 5; private AssetManager mAssets; private List<Sound> mSounds = new ArrayList<>(); private SoundPool mSoundPool; public BeatBox(Context context) { mAssets = context.getAssets(); /** * Create SoundPool * @param maxStreams int Maximum number of simultaneous audio playback * @param streamType int Audio stream type, AudioManager STREAM_ Music represents music and game audio streams * @param srcQuality int Sample rate conversion quality is invalid now. 0 is passed in */ mSoundPool = new SoundPool(MAX_SOUNDS, AudioManager.STREAM_MUSIC, 0); loadSounds(); } //... }
Load audio file
Next, use SoundPool to load the audio file. Compared with other audio playback methods, SoundPool also has the advantage of fast response: as soon as the command is issued, it will start playing immediately. However, it needs to preload the audio before playing.
Audio files loaded by SoundPool have their own Integer type ID. Add the SoundId instance variable in the Sound class, and add the corresponding get method and set method to manage these IDs.
Sound.java
public class Sound { private String mAssetPath; private String mName; private Integer mSoundId; //... public Integer getSoundId() { return mSoundId; } public void setSoundId(Integer soundId) { mSoundId = soundId; } }
mSoundId uses Integer type instead of int. In this way, you can set the mSoundId of Sound to a null value when it has no value.
Load audio
BeatBox.java
//Load audio private void loadSound(Sound sound) throws IOException { AssetFileDescriptor afd = mAssets.openFd(sound.getAssetPath()); int soundId = mSoundPool.load(afd, 1); sound.setSoundId(soundId); }
Call msoundpool The load (assetfiledescriptor, int) method can load the file into the SoundPool to be played. To facilitate managing, replaying, or uninstalling audio files, msoundpool The load (...) method returns an int ID. This is actually the ID stored in mSoundId. Calling the openFd(String) method may throw IOException, and so is the load(Sound) method.
Load all audio files
BeatBox.java
//Get the asset resource, save it in the list, and load all audio files private void loadSounds() { String[] soundNames; //Get the resource list in assets //... for (String filename : soundNames) { try { String assetPath = SOUNDS_FOLDER + "/" + filename; Sound sound = new Sound(assetPath); loadSound(sound); mSounds.add(sound); } catch (IOException ioe) { Log.e(TAG, "Unable to load sound resources " + filename, ioe); } } }
Play audio
BeatBox.java
public void playSound(Sound sound) { Integer soundId = sound.getSoundId(); if (soundId == null) return; /** * Play the audio file with the specified ID * @param soundID int load(...)Method loads the return value of the audio * @param leftVolume float Left volume (range = 0.0 to 1.0) * @param rightVolume float Right volume (range = 0.0 to 1.0) * @param priority int Priority, invalid (0 = lowest priority) * @param loop int Loop (0 = no loop, -1 = loop forever) * @param rate float Playback rate (1.0 = normal playback, range 0.5 to 2.0) * @return int ID is returned successfully, and 0 is returned for failure */ mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f); }
Add button click event
BeatBoxFragment.SoundHolder
private class SoundHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private Button mButton; private Sound mSound; public SoundHolder(LayoutInflater layoutInflater, ViewGroup container) { super(layoutInflater.inflate(R.layout.list_item_sound, container, false)); mButton = itemView.findViewById(R.id.list_item_sound_button); mButton.setOnClickListener(this); } //Bind sound public void bindSound(Sound sound) { mSound = sound; mButton.setText(mSound.getName()); } @Override public void onClick(View v) { mBeatBox.playSound(mSound); } }
Release audio
BeatBox.java
public void releasePool() { mSoundPool.release(); }
Override onDesreoy() method and release SoundPool in it:
BeatBoxFragment.java
@Override public void onDestroy() { super.onDestroy(); mBeatBox.releasePool(); }
Device rotation and object saving
In the Java world, to save an object, either put it into a Bundle or implement the Serializable interface or the Parcelable interface. Either way, the object must first be a saveable object.
Some part of the BeatBox can be saved. For example, everything in the Sound class can be saved; SoundPool cannot be saved. Although you can create a SoundPool containing the same audio file or continue from the audio playback interruption, like the TV playback example, the time of playback interruption can not be found anyway.
Keep fragment
The savedInstanceState mechanism is only applicable to object data that can be saved, but BeatBox cannot be saved. When creating and destroying an Activity, we all need the BeatBox instance to be available all the time.
To cope with device configuration changes, fragment has a special method to ensure that the BeatBox instance always exists: retainInstance. Overwrite beatboxfragment Oncreate (...) method and set the property value of fragment:
BeatBoxFragment.java
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Keep fragment //In this way, when the device rotates, the retained fragment will not be destroyed with the activity. It will always be retained and passed intact to the new activity when needed. All instance variables of the retained fragment will also remain unchanged. setRetainInstance(true); mBeatBox = new BeatBox(getActivity()); }
The value of the retainInstance property of the fragment defaults to false, indicating that it will not be retained. If the attribute value is true, the view of the fragment will be destroyed immediately, but the fragment itself will not be destroyed. Although the retained fragment has not been destroyed, it has separated from the dying activity and is in a retained state. Although the fragment still exists at this time, it is no longer hosted by any activity.
Two conditions must be met at the same time before the fragment can enter the reserved state:
- The setRetainInstance(true) method of fragment has been called;
- The managed activity is being destroyed due to a device configuration change (usually device rotation).
A fragment can only be kept for a short time, that is, the time between the fragment breaking away from the old activity and reattaching to the new activity created immediately.
In depth learning
Do you want to keep it
The use of such a mechanism is highly discouraged unless absolutely necessary.
- First, the display of retained fragments is very complex compared with non retained fragments. Once a problem occurs, troubleshooting is very time-consuming. Since using it will complicate the program, don't use it if you can.
- Secondly, fragment can also handle all life cycle scenarios when handling equipment rotation by saving the instance state; However, the retained fragment can only handle the destruction of the activity due to the rotation of the device. If the activity is destroyed because the operating system needs to reclaim memory, all retained fragments will be destroyed and the data will be lost.
Re exploration of equipment rotation processing
If there is something in the activity or fragment that needs to be saved for a long time, the onSaveInstanceState(Bundle) method should be overridden to save its state. In this way, since the lifecycle of the activity record is synchronized, the subsequent can be restored when needed.