【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方法
public void 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方法
private void startImpl() { baseStart(); stayAwake( true); _start(); }

private native void _start() throws IllegalStateException;

对应的JNI方法为
这里没有使用 NewGlobalRef对Java层对象进行保留操作
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值