【Android】声音播放截断(骤停)的问题

这个故事需要从头说起,Jack接到了一个需求,要对Android系统增加开机铃声,
系统中没有设置开机铃声的地方,修改在native层开机动画启动的地方时,时间点不好控制,会出现铃声播放结束了,但是还没有开机,手机还是停留在开机动画上,这个时候,就是尴尬的等待。怎么还不开机~~
Jack 查看了开机流程,很机灵的想到了一个主意,在系统启动完成的时候,去调用音频播放。
效果还不错。在动画结束,画面切换到使用界面的时候,铃声一下子就开始播放了。这个用户体验还可以。毕竟铃声只有短短的几秒钟。

后来Jack跳槽了,。
当系统升级的时候,采用同样的方法,
开机铃声出现了截断,这个问题转手了几次,很不幸的让我来处理了

Jack是在AMS的SystemReady方法中调用播放方法playbootaudio,为了不影响系统进程,Jack还启动了一个线程来执行这个调用
public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
...
new Thread(new Runnable(){
public void run(){
playbootaudio();
}
}).start();
...
}

Jack写的playbootaudio()方法看上去也没有问题,在其他地方调用这个方法进行测试,声音可以正常播放
    private void playbootaudio(){
    MediaPlayer mp = new MediaPlayer();
        mp.setOnCompletionListener(new OnCompletionListener(){                   
          public void onCompletion(MediaPlayer mpCallback) {
Log.d(TAG, "====== audio play onCompletion called");
           mpCallback.release();
        
          }
         });
 
                 
    try {
Log.d(TAG, "====== audio play called");
        mp.setDataSource("/system/media/audio.mp3");
        mp.setAudioStreamType(AudioManager.STREAM_ALARM);//STREAM_MUSIC STREAM_ALARM
        mp.prepare();
        mp.start();
         }catch (Exception e) {
Log.e(TAG, "===== playbootaudio error");
         e.printStackTrace();
         }

    }

添加log进行测试,抓取开机时的log,发现Log.d(TAG, "====== audio play onCompletion called");并没有打印

也就是说onCompletion方法没有调用。
奇怪了,怎么会这样,
这个时候,多年的编程经验终于起了点作用,
难道是对象被释放了?看上去好像有这个可能。
于是在MediaPlayer的finalize()方法中添加log,
结果,还真是调到这里了
01-01 12:19:46.625528  1012  1020 D MediaPlayer: =========== MediaPlayer, finalize() called

现在就好办了,
代码修改为
private MediaPlayer mp;
    private void playbootaudio(){
//    MediaPlayer mp = new MediaPlayer();
                                mp = new MediaPlayer();
        mp.setOnCompletionListener(new OnCompletionListener(){                   
          public void onCompletion(MediaPlayer mpCallback) {
Log.d(TAG, "====== audio play onCompletion called");
           mpCallback.release();
           mp = null;
          }
         });
 
                 
    try {
Log.d(TAG, "====== audio play called");
        mp.setDataSource("/system/media/bootaudio.mp3");
        mp.setAudioStreamType(AudioManager.STREAM_ALARM);//STREAM_MUSIC STREAM_ALARM
        mp.prepare();
//Log.d(TAG, "====== audio play  mp.start()");
        mp.start();
         }catch (Exception e) {
Log.e(TAG, "===== playbootaudio error");
         e.printStackTrace();
         }
//Log.e(TAG, "===== end of playbootaudio()");
    }

原来是局部变量被回收,导致不能播放铃声,
这是个非常经典的案例,
一般情况下,想找到这种例子都是比较困难的
踏破铁鞋无觅处得来全不费工夫

再来看看MediaPlayer的start方法
publicvoid start() throws IllegalStateException { //FIXME use lambda to pass startImpl to superclass final int delay = getStartDelayMs(); if (delay ==0) { startImpl(); } else { new Thread() { public void run() { try { Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } baseSetStartDelayMs(0);try { startImpl(); } catch (IllegalStateException e) {// fail silently for a state exception when it is happening after// a delayed start, as the player state could have changed between the// call to start() and the execution of startImpl() } } }.start(); } }

调用到startInmp方法
privatevoid startImpl() { baseStart(); stayAwake(true);_start(); }

privatenative void_start() throws IllegalStateException;

对应的JNI方法为
这里没有使用NewGlobalRef对Java层对象进行保留操作
发布了336 篇原创文章 · 获赞 14 · 访问量 33万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览