1. 程式人生 > >Android中Ringtone播放詳解【安卓原始碼解析五】

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 plaincopyprint?
  1.  <span style="FONT-SIZE: 16px">Intent intent = 
    new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);    
  2.         // Allow user to pick 'Default'   
  3.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);    
  4.         // Show only ringtones   
  5.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);    
  6.         //set the default Notification value   
  7.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));    
  8.         // Don't show 'Silent'   
  9.         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);    
  10.         startActivityForResult(intent, SMS_RINGTONE_PICKED);    
  11. </span>  


這個activity啟動後就跑到了framework的com.android.internal.app.RingtonePickerActivity裡面去了,在framework部分也可以寫activity類得,這個挺有意思的,哈哈,新發現,有種“山窮水復疑無路,柳暗花明又一村”的感覺!哈哈,這個intent的action發到哪兒去了呢??

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

[html] view plaincopyprint?
  1. <activityandroid: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.          <actionandroid:name="android.intent.action.RINGTONE_PICKER"/>
  7.          <categoryandroid: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 plaincopyprint?
  1. publicfinalclass RingtonePickerActivity extends AlertActivity implements
  2.         AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,  
  3.         AlertController.AlertParams.OnPrepareListViewListener  

這個類繼承AlertActivity並實現了這麼多介面,找到點選每個選項播放的方法監聽:

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

然後找到playRingtone(which, 0);這個方法去:

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

這時候找到這裡就基本上找到根源了,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?
  1. publicvoid 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 plaincopyprint?
  1. privatevoid openMediaPlayer() throws IOException {  
  2.         mAudio = new MediaPlayer();  
  3.         if (mUri != null) {  
  4.             mAudio.setDataSource(mContext, mUri);  
  5.         } elseif (mFileDescriptor != null) {  
  6.             mAudio.setDataSource(mFileDescriptor);  
  7.         } elseif (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

    相關推薦

    AndroidRingtone播放原始碼解析

            現在咱們來聊聊android系統中鈴聲的播放,從framework層面說說Ringtone的播放原理,我在android原始碼中碰到了播放系統鈴聲中的問題,所以仔細研究了這方面的知識,現在整理一下,給讀者一些幫助,現在我對鈴聲的播放和設定特別親切,不管是簡

    Android的動畫系列1——逐幀動畫

    逐幀動畫其實很簡單,下面我們來看一個例子:<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:andro

    Eclipse的BuildPath轉載

    sdn libraries 依賴 源代碼 title 打開 低版本 代碼 功能 什麽是Build Path? Build Path是指定Java工程所包含的資源屬性集合。 在一個成熟的Java工程中,不僅僅有自己編寫的源代碼,還需要引用系統運行庫(JRE)、第三方的功能擴展

    androidwifi原理(轉)

    二:Wifi模組的初始化:: 在 SystemServer 啟動的時候,會生成一個ConnectivityService的例項, try { Log.i(TAG, "Starting Connectivity Service."); ServiceManager.addService(Con

    AndroidJNI使用(4)---Java與C之間資料型別轉換

    Jni中基本型別轉換對應的表格 Java型別 本地型別 說明 boolean jboolean 無符號,8位 byte jbyte

    AndroidJNI使用(3)---Android StudioSO檔案生成

    Android中JNI使用詳解(2)---Android Studio中SO檔案生成 上一篇寫到過在Android Studio中配置NDK環境地址:Android Studio中NDK環境配置 這篇文章講解在Android Studio中

    AndroidJNI使用(2)---Android StudioNDK環境配置

    Android Studio中的NDK環境配置 1、下載NKD 在Android Studio中選擇File----Settings----Appearance&Behavior---System Settings----Andr

    AndroidJNI使用(1)---EclipseNDK配置So檔案生成

    1、NDK下載和配置 NDK下載地址:http://www.androiddevtools.cn/ NDK下載完成後,選擇Eclipse上方Window選單Preferences - Android - NDK 在NDK&nb

    Android的Service

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    androidwifi原理

                    二:Wifi模組的初始化::在 SystemServer 啟動的時候,會生成一個ConnectivityService的例項,try {Log.i(TAG, "Starting Connectivity Service.");ServiceManager.addService(

    AndroidSQLite應用

    上次我向大家介紹了SQLite的基本資訊和使用過程,相信朋友們對SQLite已經有所瞭解了,那今天呢,我就和大家分享一下在Android中如何使用SQLite。 現在的主流移動裝置像Android、iPhone等都使用SQLite作為複雜資料的儲存引擎,在我們為移動裝置開發

    Http——AndroidHttpURLConnection使用

    認識Http協議 ndroid中傳送http網路請求是很常見的,要有GET請求和POST請求。一個完整的http請求需要經歷兩個過程:客戶端傳送請求到伺服器,然後伺服器將結果返回給客戶端,如下圖所示: 客戶端->伺服器  客戶端向伺服器傳送請求主要包含以下資

    AndroidAsyncTask使用

    在Android中我們可以通過Thread+Handler實現多執行緒通訊,一種經典的使用場景是:在新執行緒中進行耗時操作,當任務完成後通過Handler向主執行緒傳送Message,這樣主執行緒的Handler在收到該Message之後就可以進行更新UI的操作

    [轉載]AndroidAndroidManifest.xml

    一、關於AndroidManifest.xml AndroidManifest.xml 是每個android程式中必須的檔案。它位於整個專案的根目錄,描述了package中暴露的元件(activities, services, 等等),他們各自的實現類,各種能被處理的資料和

    Android HttpURLConnection 使用

    認識Http協議 Android中傳送http網路請求是很常見的,要有GET請求和POST請求。一個完整的http請求需要經歷兩個過程:客戶端傳送請求到伺服器,然後伺服器將結果返回給客戶端,如下圖所示: 客戶端->伺服器 客戶端向伺服器傳送請求主要包含以下

    AndroidService(服務)

    Service是Android中四大元件之一,在Android開發中起到非常重要的作用,先來看一下官方對Service的定義: A  is an application component that can perform long-running operation

    Android垃圾回收

      在這個例子中,一個新的物件被建立,由於它沒有使用,所以該物件迅速地變為不可達,程式編譯後,執行命令: java -verbosegc TestGC 後結果為:  [Full GC 168K->97K(1984K), 0.0253873 secs]  機器的環境為,Windows 2000 + JDK

    Android的Binder

    Binder簡介 由於Binder在Android的資訊傳輸中佔有比較重要的作用,所以把對Binder的分析單獨出一篇文章來記錄一下。 什麼是Binder Binder,翻譯為粘合劑,在Android程序間通訊相關的知識中經常出現。一般來說對Binder

    Androidshape屬性

    一、簡單使用 剛開始,就先不講一堆標籤的意義及用法,先簡單看看shape標籤怎麼用。 1、新建shape檔案 首先在res/drawable資料夾下,新建一個檔案,命名為:shape_radius.xml 內容是這樣的:(先不需要理解,先看sh

    AndroidContentProvider元件

    1.ContentProvider Android提供了一些主要資料型別的ContentProvider,比如音訊、視訊、圖片和私人通訊錄等。可在android.provider包下面找到一些Android提供的ContentProvider。通過獲得這些ContentProvider可以查詢它們包含的資料,