1. 程式人生 > >記錄小米 4,錘子手機上播放系統鈴聲失敗的問題

記錄小米 4,錘子手機上播放系統鈴聲失敗的問題

最近遇到一個問題,發現部分機型如:小米 4,錘子 Pro2 呼叫如下程式碼播放系統鈴聲失敗:

try {
    Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE);
    mediaPlayer.setDataSource(context, defaultRingtoneUri);
    mediaPlayer.prepare();
    mediaPlayer.start();
} catch (IOException e) {
    e.printStackTrace
(); }

日誌中捕獲瞭如下異常資訊:

System.err: java.io.IOException: setDataSource failed.
W/System.err:     at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1101)
W/System.err:     at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1047)
W/System.err:     at android.media.MediaPlayer.setDataSource
(MediaPlayer.java:992) W/System.err: at android.media.MediaPlayer.setDataSource(MediaPlayer.java:971) W/System.err: at com.example.rongcloud.myapplication.ActivityA.onCreate(ActivityA.java:30) W/System.err: at android.app.Activity.performCreate(Activity.java:6801) W/System.err: at android.app
.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2686) W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2794) W/System.err: at android.app.ActivityThread.-wrap12(ActivityThread.java) W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1541) W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102) W/System.err: at android.os.Looper.loop(Looper.java:163) W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6358) W/System.err: at java.lang.reflect.Method.invoke(Native Method) W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901) W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791)

發現這個問題後首先就是去打斷點看下獲取到的 Uri 是否有問題:
- 小米 4 上獲取到的 uri 為:file:///system/media/audio/ringtones/MI.ogg
- 錘子 pro2 上獲取到的 uri 為:null

錘子手機獲取到的 uri 為 null,呼叫 setDataSource 失敗可以理解,然而小米手機獲取到的 uri 不為 null 為什麼呼叫 setDataSource 還會失敗?

為一探究竟,想到可以使用 adb shell 檢視手機中是否存在這個檔案:

admindeMacBook-Pro-2:~ admindeMacBook$ adb shell
meri:/ $ cd system/media/audio/ringtones/                                      
meri:/system/media/audio/ringtones $ ls
AcousticGuitar.ogg Country.ogg      Lollipop.ogg    NewDay.ogg    
Bells.ogg          Echo.ogg         MIX.ogg         Orange.ogg    
Blues.ogg          Enthusiastic.ogg Mi.ogg          Raindrops.ogg 
Breeze.ogg         Fantasy.ogg      MiGlass.ogg     Sunrise.ogg   
Carousel.ogg       Future.ogg       MiRemix.ogg     Thinker.ogg   
Celesta.ogg        Glockenspiel.ogg MiXylophone.ogg ToyRobot.ogg  
Childhood.ogg      Leisure.ogg      MusicBox.ogg    
meri:/system/media/audio/ringtones $ 

從這裡可以發現 ringtones 目錄下是沒有 MI.ogg 這個檔案的,但卻存在一個 Mi.ogg 檔案,原來是檔名稱不匹配導致的!(應該是系統的一個 Bug)

既然找到了問題的導致原因,該如何解決呢?

檢視 RingtoneManager 可以發現還有一個介面:

RingtoneManager.getValidRingtoneUri(Context context)

對應的文件描述:

/**
     * Returns a valid ringtone URI. No guarantees on which it returns. If it
     * cannot find one, returns null. If it can only find one on external storage and the caller
     * doesn't have the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission,
     * returns null.
     *
**/

意思就是說該方法能夠確保返回一個有效的 uri,如果不能找到一個有效的 uri 則返回 null。

知道這個介面後可以這樣做:

Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE);
try {
    mediaPlayer.setDataSource(context, defaultRingtoneUri);
    mediaPlayer.prepare();
    mediaPlayer.start();
} catch (IOException e) {
    defaultRingtoneUri = RingtoneManager.getValidRingtoneUri(context);
    mediaPlayer.setDataSource(context, defaultRingtoneUri);
    mediaPlayer.prepare();
    mediaPlayer.start();
}

這樣問題就解決了,即便播放系統鈴聲失敗也不至於什麼聲音都沒有。