Android中Ringtone播放详解【安卓源码解析五】

        现在咱们来聊聊android系统中铃声的播放,从framework层面说说Ringtone的播放原理,我在android源码中碰到了播放系统铃声中的问题,所以仔细研究了这方面的知识,现在整理一下,给读者一些帮助,现在我对铃声的播放和设置特别亲切,不管是短信铃声方面的,来电铃声,还是日历铃声,email的铃声,闹钟的铃声,都能解决铃声方面的bug。前面我说了Notification的播放机制,Android中Notification的framework层讲解【安卓源码解析四】 .  大概给大家说了说,有想了解的可以参考看一看。铃声播放的机制都是用MediaPlayer来播放的,通过MediaPlayer来申请AudioManager机制来播放音乐铃声的。前面我写了个调用系统铃声和sdcard卡中铃声的demo,Android中铃声总结【安卓源码解析一】.这个例子中有一段代码:通过intent的action来启动一个选择系统铃声的dialog,这个到底是启动的哪儿的dialog,今天给大家揭秘一下,希望给大家带来点帮助:大明原创,转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7166134

      写一个intent来启动播放ringtone的activity:   

[java]  view plain copy print ?
  1.  <span style="FONT-SIZE: 16px">Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);    
  2.   
  3.         // Allow user to pick 'Default'     
  4.   
  5.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);    
  6.   
  7.         // Show only ringtones     
  8.   
  9.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);    
  10.   
  11.         //set the default Notification value     
  12.   
  13.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));    
  14.   
  15.         // Don't show 'Silent'     
  16.   
  17.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);    
  18.   
  19.         startActivityForResult(intent, SMS_RINGTONE_PICKED);    
  20. </span>  


这个activity启动后就跑到了framework的com.android.internal.app.RingtonePickerActivity里面去了,在framework部分也可以写activity类得,这个挺有意思的,哈哈,新发现,有种“山穷水复疑无路,柳暗花明又一村”的感觉!哈哈,这个intent的action发到哪儿去了呢??

 

           在frameworks\base\core\res\AndroidManifest.xml中可以搜到这个action:    

[html]  view plain copy print ?
  1. <activity android:name="com.android.internal.app.RingtonePickerActivity"  
  2.          android:theme="@style/Theme.Dialog.Alert"  
  3.          android:excludeFromRecents="true"  
  4.          android:multiprocess="true">  
  5.      <intent-filter>  
  6.          <action android:name="android.intent.action.RINGTONE_PICKER" />  
  7.          <category android:name="android.intent.category.DEFAULT" />  
  8.      </intent-filter>  
  9.  </activity>  

<action android:name="android.intent.action.RINGTONE_PICKER" />这个就是接受这个intent的action动作的对应的activity的类,找这个RingtonePickerActivity.java类,这个类的继承和实现有点特殊,注意观察:

[java]  view plain copy print ?
  1. public final class RingtonePickerActivity extends AlertActivity implements  
  2.         AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,  
  3.         AlertController.AlertParams.OnPrepareListViewListener  

这个类继承AlertActivity并实现了这么多接口,找到点击每个选项播放的方法监听:

[java]  view plain copy print ?
  1. private DialogInterface.OnClickListener mRingtoneClickListener =  
  2.         new DialogInterface.OnClickListener() {  
  3.   
  4.     /* 
  5.      * On item clicked 
  6.      */  
  7.     public void onClick(DialogInterface dialog, int which) {  
  8.         // Save the position of most recently clicked item  
  9.         mClickedPos = which;  
  10.           
  11.         // Play clip  
  12.         playRingtone(which, 0);  
  13.     }  
  14.       
  15. };  

然后找到playRingtone(which, 0);这个方法去:

[java]  view plain copy print ?
  1. private void playRingtone(int position, int delayMs) {  
  2.        mHandler.removeCallbacks(this);  
  3.        mSampleRingtonePos = position;  
  4.        mHandler.postDelayed(this, delayMs);  
  5.    }  

这个postDelayed(this, delayMs);this代表当前对象,因为这个类是实现了Runnable接口,所以这时候会走到run方法中去

[java]  view plain copy print ?
  1. public void run() {  
  2.          
  3.        if (mSampleRingtonePos == mSilentPos) {  
  4.            mRingtoneManager.stopPreviousRingtone();  
  5.            return;  
  6.        }  
  7.          
  8.        /* 
  9.         * Stop the default ringtone, if it's playing (other ringtones will be 
  10.         * stopped by the RingtoneManager when we get another Ringtone from it. 
  11.         * by bw on start  
  12.         * when it`s not null set the default ringtone is null 
  13.         * modify by wangxianming in 2011-12-28 
  14.         */  
  15.        if (mDefaultRingtone != null) {  
  16. //            if(mDefaultRingtone.isPlaying()){  
  17.                mDefaultRingtone.stop();  
  18. //           }  
  19.            mDefaultRingtone = null;  
  20.        }  
  21.          
  22.        Ringtone ringtone;  
  23.        if (mSampleRingtonePos == mDefaultRingtonePos) {  
  24.            if (mDefaultRingtone == null) {  
  25.                mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);  
  26.            }  
  27.            ringtone = mDefaultRingtone;  
  28.              
  29.            /* 
  30.             * Normally the non-static RingtoneManager.getRingtone stops the 
  31.             * previous ringtone, but we're getting the default ringtone outside 
  32.             * of the RingtoneManager instance, so let's stop the previous 
  33.             * ringtone manually. 
  34.             */  
  35.            mRingtoneManager.stopPreviousRingtone();  
  36.              
  37.        } else {  
  38.            ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));  
  39.        }  
  40.          
  41.        if (ringtone != null) {  
  42.            ringtone.play();  
  43.        }  
  44.    }  

这时候找到这里就基本上找到根源了,ringtone.play();就是播放ringtone音乐的方法;注意:15行,16行,我对源码进行了修改,这个有点问题,源码对原生态的音乐格式支持的很好,但是对mid格式的音乐支持的不太好,所以如果播放mid格式的音乐不能用多个mediaplayer来播放,只能用一个来播放,只能hold住一个,不能重复hold这个ringtone,所以把这个mDefaultRingtone = null;每次都置为空,这样每次都是一个对象就可以了。这样播放系统铃声就没有问题了!


 

       再去frameworks\base\media\java\android\media\Ringtone.java这个类去找到play()方法:   

[java]  view plain copy print ?
  1. public void play() {  
  2.         if (mAudio == null) {  
  3.             try {  
  4.                 openMediaPlayer();  
  5.             } catch (Exception ex) {  
  6.                 Log.e(TAG, "play() caught ", ex);  
  7.                 mAudio = null;  
  8.             }  
  9.         }  
  10.         if (mAudio != null) {  
  11.             // do not ringtones if stream volume is 0  
  12.             // (typically because ringer mode is silent).  
  13.             if (mAudioManager.getStreamVolume(mStreamType) != 0) {  
  14.                 if (mIsLoop){  
  15.                     mAudio.setLooping(true);              
  16.                 }     
  17.                 mAudio.start();  
  18.             }  
  19.         }  
  20.     }  

找到openMediaPlayer()方法 :

[java]  view plain copy print ?
  1. private void openMediaPlayer() throws IOException {  
  2.         mAudio = new MediaPlayer();  
  3.         if (mUri != null) {  
  4.             mAudio.setDataSource(mContext, mUri);  
  5.         } else if (mFileDescriptor != null) {  
  6.             mAudio.setDataSource(mFileDescriptor);  
  7.         } else if (mAssetFileDescriptor != null) {  
  8.             // Note: using getDeclaredLength so that our behavior is the same  
  9.             // as previous versions when the content provider is returning  
  10.             // a full file.  
  11.             if (mAssetFileDescriptor.getDeclaredLength() < 0) {  
  12.                 mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor());  
  13.             } else {  
  14.                 mAudio.setDataSource(mAssetFileDescriptor.getFileDescriptor(),  
  15.                         mAssetFileDescriptor.getStartOffset(),  
  16.                         mAssetFileDescriptor.getDeclaredLength());  
  17.             }  
  18.         } else {  
  19.             throw new IOException("No data source set.");  
  20.         }  
  21.         mAudio.setAudioStreamType(mStreamType);  
  22.         mAudio.prepare();  
  23.     }  

这样就把所有的ringtone播放的机制大致能搞清楚了!希望这个流程给大家一些启发!有问题的可以留言,我看到会给出解释的,希望高手指点不足之处!大明原创。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值