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

来源:互联网 发布:淘宝上做什么产品好 编辑:程序博客网 时间:2024/04/30 19:38
 现在咱们来聊聊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 plaincopyprint?
 <span style="FONT-SIZE: 16px">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);    
</span>  


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


 


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


[html] view plaincopyprint?
<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" />这个就是接受这个intent的action动作的对应的activity的类,找这个RingtonePickerActivity.java类,这个类的继承和实现有点特殊,注意观察:


[java] view plaincopyprint?
public final class RingtonePickerActivity extends AlertActivity implements  
        AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,  
        AlertController.AlertParams.OnPrepareListViewListener  
这个类继承AlertActivity并实现了这么多接口,找到点击每个选项播放的方法监听:


[java] view plaincopyprint?
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);  
    }  
      
};  
然后找到playRingtone(which, 0);这个方法去:


[java] view plaincopyprint?
private void playRingtone(int position, int delayMs) {  
       mHandler.removeCallbacks(this);  
       mSampleRingtonePos = position;  
       mHandler.postDelayed(this, delayMs);  
   }  
这个postDelayed(this, delayMs);this代表当前对象,因为这个类是实现了Runnable接口,所以这时候会走到run方法中去:


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




 


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


[java] view plaincopyprint?
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();  
            }  
        }  
    }  
找到openMediaPlayer()方法 :


[java] view plaincopyprint?
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();  
    }  
这样就把所有的ringtone播放的机制大致能搞清楚了!希望这个流程给大家一些启发!有问题的可以留言,我看到会给出解释的,希望高手指点不足之处!大明原创。
原创粉丝点击