Now let's talk about playing ringtones in the Android system. From the framework level, let's talk about the playing principle of ringtone. I encountered a problem in playing system ringtones in the android source code, so I carefully studied this knowledge. Now I have sorted it out and provided some help to the readers. Now I am very friendly in playing and setting ringtones, whether it is for text message ringtones or for incoming call ringtones, you can also use calendar ringtones, email ringtones, and alarm clocks to solve the ringtone bug. I mentioned the playback mechanism of notification,Android notification framework layer [Android source code parsing 4]. I probably told you about it. If you want to know it, please refer to it. The playing mechanism of ringtones is played by mediaplayer. mediaplayer is used to apply for the audiomanager mechanism to play music ringtones. I wrote a demo called the system ringtone and the ringtone IN THE sdcard card,Summary of ringtones in Android [Android source code analysis 1]. In this example, there is a piece of code: Use the intent action to start a di for selecting the system ringtone. This is where the dialog is started. I will reveal it to you today, hope to bring you some help: Daming original, reprinted please indicate the source: http://blog.csdn.net/wdaming1986/article/details/7166134
Write an intent to start playing the ringtone activity:
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); // Allow user to pick 'Default' intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); // Show only ringtones intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); //set the default Notification value intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)); // Don't show 'Silent' intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); startActivityForResult(intent, SMS_RINGTONE_PICKED);
After the activity is started, it runs to the com. android. internal. app. ringtonepickeractivity went in. In the framework part, you can also write activity classes. This is very interesting. Haha, New Discoveries come with the feeling that "the mountains are poor and there is no way to go, and there is no way to go! Haha, where did the intent action go ??
You can find this action in frameworks \ base \ core \ res \ androidmanifest. xml:
<activity android:name="com.android.internal.app.RingtonePickerActivity" android:theme="@style/Theme.Dialog.Alert" android:excludeFromRecents="true" android:multiprocess="true"> <intent-filter> <action android:name="android.intent.action.RINGTONE_PICKER" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
<Action Android: Name = "android. intent. action. ringtone_picker "/> This is the class of the activity that accepts the action of the intent. Find This ringtonepickeractivity. java class. The Inheritance and implementation of this class are a bit special. Note the following:
public final class RingtonePickerActivity extends AlertActivity implements AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener, AlertController.AlertParams.OnPrepareListViewListener
This class inherits alertactivity and implements so many interfaces, find the method to listen for playing by clicking each option:
private DialogInterface.OnClickListener mRingtoneClickListener = new DialogInterface.OnClickListener() { /* * On item clicked */ public void onClick(DialogInterface dialog, int which) { // Save the position of most recently clicked item mClickedPos = which; // Play clip playRingtone(which, 0); } };
Then find playringtone (which, 0); this method goes:
private void playRingtone(int position, int delayMs) { mHandler.removeCallbacks(this); mSampleRingtonePos = position; mHandler.postDelayed(this, delayMs); }
This postdelayed (this, delayms); this represents the current object, because this class implements the runnable interface, so this will go to the run method:
public void run() { if (mSampleRingtonePos == mSilentPos) { mRingtoneManager.stopPreviousRingtone(); return; } /* * Stop the default ringtone, if it's playing (other ringtones will be * stopped by the RingtoneManager when we get another Ringtone from it. * by bw on start * when it`s not null set the default ringtone is null * modify by wangxianming in 2011-12-28 */ if (mDefaultRingtone != null) {/// if(mDefaultRingtone.isPlaying()){ mDefaultRingtone.stop();/// } mDefaultRingtone = null; } Ringtone ringtone; if (mSampleRingtonePos == mDefaultRingtonePos) { if (mDefaultRingtone == null) { mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem); } ringtone = mDefaultRingtone; /* * Normally the non-static RingtoneManager.getRingtone stops the * previous ringtone, but we're getting the default ringtone outside * of the RingtoneManager instance, so let's stop the previous * ringtone manually. */ mRingtoneManager.stopPreviousRingtone(); } else { ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos)); } if (ringtone != null) { ringtone.play(); } }
At this time, the root cause is basically found here. ringtone. Play (); is the method for playing ringtone music;Note:: 15 lines and 16 lines. I have modified the source code. This is a bit of a problem. The source code supports the original music format very well, but it does not support the mid music, therefore, if you cannot play mid-format music using multiple mediaplayers, you can only play one of them. You can only hold one of them and cannot hold the ringtone repeatedly. Therefore, set mdefaultringtone to null; set it to null every time, so that it can be an object every time. In this way, the playing system ringtones will be okay!
Go to frameworks \ base \ media \ Java \ Android \ media \ ringtone. Java to find the play () method:
public void play() { if (mAudio == null) { try { openMediaPlayer(); } catch (Exception ex) { Log.e(TAG, "play() caught ", ex); mAudio = null; } } if (mAudio != null) { // do not ringtones if stream volume is 0 // (typically because ringer mode is silent). if (mAudioManager.getStreamVolume(mStreamType) != 0) { if (mIsLoop){ mAudio.setLooping(true); } mAudio.start(); } } }
Find the openmediaplayer () method:
private void openMediaPlayer() throws IOException { mAudio = new MediaPlayer(); if (mUri != null) { mAudio.setDataSource(mContext, mUri); } else if (mFileDescriptor != null) { mAudio.setDataSource(mFileDescriptor); } else if (mAssetFileDescriptor != null) { // Note: using getDeclaredLength so that our behavior is the same // as previous versions when the content provider is returning // a full file. if (mAssetFileDescriptor.getDeclaredLength() < 0) { mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor()); } else { mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor(), mAssetFileDescriptor.getStartOffset(), mAssetFileDescriptor.getDeclaredLength()); } } else { throw new IOException("No data source set."); } mAudio.setAudioStreamType(mStreamType); mAudio.prepare(); }
In this way, we can figure out all the ringtone playing mechanisms! I hope this process will inspire you! If you have any questions, please leave a message. I will give an explanation. I hope you can give some advice! Daming original.