博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android6.0.1音乐apk获得audioFocus的流程
阅读量:4284 次
发布时间:2019-05-27

本文共 15792 字,大约阅读时间需要 52 分钟。

在android6.0.1版本上,使用了audioFocus的方式来抢占音频使用权;那么各个音频apk就要遵守 audioFocus的各种规定和用法。

下面分几个流程说明一下各个情况下的 audioFocus使用。

在audio系统中,上层各个功能部分的分层大概如下图所示:

其中,针对audioFocus功能,在当打开Music 的 apk时,这个apk主动向 AudioManager 申请  audioFocus,AudioManager再向audioService申请audioFocus;在这个机制中,AudioManager只是一个接口,是audio系统对上层 apk 的接口的封装,便于上层 apk调用。

具体流程看下面的代码分析:

一、直接播放音乐时,申请 audioFocus

1. Music 的 apk部分代码分析:

代码路径是:/packages/apps/Music/下;

当打开Music.apk,播放音乐的时候,首先执行初始化工作,这个初始化是整个播放器的初始化,不仅仅是audioFocus

public void onCreate() {        super.onCreate();        if(!PermissionUtils.hasPermissions(this,PermissionUtils.getDesiredPermissions())){            System.out.println("==========cuijc3=========servcie oncreate() stop self");            stopSelf();            Log.e(LOGTAG, "zsm onCreate error");            return;        }        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);        ComponentName rec = new ComponentName(getPackageName(),                MediaButtonIntentReceiver.class.getName());        mAudioManager.registerMediaButtonEventReceiver(rec);

从AudioManager接收消息的函数,包括其它功能;不仅仅是 audioFocus:

private Handler mMediaplayerHandler = new Handler() {        float mCurrentVolume = 1.0f;        @Override        public void handleMessage(Message msg) {            MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);            Log.e(LOGTAG, "zsm handleMessange");            switch (msg.what) {                case FADEDOWN:                    mCurrentVolume -= .05f;                    if (mCurrentVolume > .2f) {                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEDOWN, 10);                    } else {                        mCurrentVolume = .2f;                    }                    mPlayer.setVolume(mCurrentVolume);                    break;                case FADEUP:                    mCurrentVolume += .01f;                    if (mCurrentVolume < 1.0f) {                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEUP, 10);                    } else {                        mCurrentVolume = 1.0f;                    }                    mPlayer.setVolume(mCurrentVolume);                    break;                case SERVER_DIED:                    if (mIsSupposedToBePlaying) {                        gotoNext(true);                    } else {                        // the server died when we were idle, so just                        // reopen the same song (it will start again                        // from the beginning though when the user                        // restarts)                        openCurrentAndNext();                    }                    break;                case TRACK_WENT_TO_NEXT:                    mPlayPos = mNextPlayPos;                    if (mCursor != null) {                        mCursor.close();                        mCursor = null;                    }                    if (mPlayPos >= 0 && mPlayPos < mPlayList.length) {                        mCursor = getCursorForId(mPlayList[mPlayPos]);                    }                    notifyChange(META_CHANGED);                    updateNotification();                    setNextTrack();                    break;                case TRACK_ENDED:                    if (mRepeatMode == REPEAT_CURRENT) {                        seek(0);                        play();                    } else {                        gotoNext(false);                    }                    break;                case RELEASE_WAKELOCK:                    mWakeLock.release();                    break;                case FOCUSCHANGE:  // 这部分是audioFocus的状态改变                    // This code is here so we can better synchronize it with the code that                    // handles fade-in                    Log.e(LOGTAG, "zsm AudioFocus: received FOCUSCHANGE");                    switch (msg.arg1) {                        case AudioManager.AUDIOFOCUS_LOSS:                            Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS");                            if(isPlaying()) {                                mPausedByTransientLossOfFocus = false;                            }                            pause();                            break;                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");                            mMediaplayerHandler.removeMessages(FADEUP);                            mMediaplayerHandler.sendEmptyMessage(FADEDOWN);                            break;                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:                            Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");                            if(isPlaying()) {                                Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT-1");                                mPausedByTransientLossOfFocus = true;                            }                            pause();                            break;                        case AudioManager.AUDIOFOCUS_GAIN:                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");                            if(!isPlaying() && mPausedByTransientLossOfFocus) {                                Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN-1");                                mPausedByTransientLossOfFocus = false;                                mCurrentVolume = 0f;                                mPlayer.setVolume(mCurrentVolume);                                play(); // also queues a fade-in                            } else {                                Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN-2");                                mMediaplayerHandler.removeMessages(FADEDOWN);                                mMediaplayerHandler.sendEmptyMessage(FADEUP);                            }                            break;                        default:                            Log.e(LOGTAG, "Unknown audio focus change code");                    }                    break;                default:                    break;            }        }    };
设置,接收消息的代码:

private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {        public void onAudioFocusChange(int focusChange) {            mMediaplayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();        }    };
这样,当底层AudioManager有什么消息来的话,就会调用那个消息函数,再根据不同的消息进行处理。

在播放之前首先申请audioFocus:

mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,                AudioManager.AUDIOFOCUS_GAIN);

2. AudioManager部分的代码分析:

上面 apk 申请audioFocus时会调用到audioManager里的函数,代码如下:

     代码路径:/frameworks/base/media/java/android/media/AudioManager.java

     经过几次 AudioManager.java 调用后会调用到以下代码函数:

@SystemApi    public int requestAudioFocus(OnAudioFocusChangeListener l,            @NonNull AudioAttributes requestAttributes,            int durationHint,            int flags,            AudioPolicy ap) throws IllegalArgumentException {        // parameter checking            Log.e(TAG, "requestAudioFocus-3: durationHint:" + durationHint);        if (requestAttributes == null) {            throw new IllegalArgumentException("Illegal null AudioAttributes argument");        }        if ((durationHint < AUDIOFOCUS_GAIN) ||                (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {            throw new IllegalArgumentException("Invalid duration hint");        }        if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {            throw new IllegalArgumentException("Illegal flags 0x"                + Integer.toHexString(flags).toUpperCase());        }        if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) {            throw new IllegalArgumentException(                    "Illegal null focus listener when flagged as accepting delayed focus grant");        }        if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {            throw new IllegalArgumentException(                    "Illegal null audio policy when locking audio focus");        }        Log.e(TAG, "requestAudioFocus-4");        int status = AUDIOFOCUS_REQUEST_FAILED;        registerAudioFocusListener(l);        IAudioService service = getService();        try {            status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,                    mAudioFocusDispatcher, getIdForAudioFocusListener(l),                    getContext().getOpPackageName() /* package name */, flags,                    ap != null ? ap.cb() : null);        } catch (RemoteException e) {            Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);        }        return status;    }
     在以上函数中,注册 AudioFocusListener; 然后调用AudioService 里的 requestAudioFocus函数进一步申请。

3. AudioService 里的代码:

      代码路径:/frameworks/base/services/core/java/com/android/server/audio/AudioService.java

      顺序调用到的函数如下:

//==========================================================================================    // Audio Focus    //==========================================================================================    public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,            IAudioPolicyCallback pcb) {        // permission checks        Log.e(TAG, "zsm: requestAudioFocus-1");        if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {            if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {                if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(                            android.Manifest.permission.MODIFY_PHONE_STATE)) {                    Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;                }            } else {                // only a registered audio policy can be used to lock focus                synchronized (mAudioPolicies) {                    if (!mAudioPolicies.containsKey(pcb.asBinder())) {                        Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");                        return AudioManager.AUDIOFOCUS_REQUEST_FAILED;                    }                }            }        }        Log.e(TAG, "zsm: requestAudioFocus-2: durationHint: " + durationHint);        return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,                clientId, callingPackageName, flags);    }
   在最后一行,调用到 MediaFocusControl 里的 requestAudioFocus 函数。 真正的申请audioFocus在 MediaFocusControl里。

4. MediaFocusControl里的代码:

    代码路径:/frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java

protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId + " req=" + focusChangeHint +                "flags=0x" + Integer.toHexString(flags));        // we need a valid binder callback for clients        if (!cb.pingBinder()) {            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;        }        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),                callingPackageName) != AppOpsManager.MODE_ALLOWED) {            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;        }        synchronized(mAudioFocusLock) {            boolean focusGrantDelayed = false;            if (!canReassignAudioFocus()) {                if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {                    return AudioManager.AUDIOFOCUS_REQUEST_FAILED;                } else {                    // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be                    // granted right now, so the requester will be inserted in the focus stack                    // to receive focus later                    focusGrantDelayed = true;                }            }            // handle the potential premature death of the new holder of the focus            // (premature death == death before abandoning focus)            // Register for client death notification            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);            try {                cb.linkToDeath(afdh, 0);            } catch (RemoteException e) {                // client has already died!                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;            }            if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {                // if focus is already owned by this client and the reason for acquiring the focus                // hasn't changed, don't do anything                final FocusRequester fr = mFocusStack.peek();                if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {                    // unlink death handler so it can be gc'ed.                    // linkToDeath() creates a JNI global reference preventing collection.                    cb.unlinkToDeath(afdh, 0);                    notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),                            AudioManager.AUDIOFOCUS_REQUEST_GRANTED);                    Log.e(TAG, "zsm: requestAudioFocus -> GRANTED");                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;                }                // the reason for the audio focus request has changed: remove the current top of                // stack and respond as if we had a new focus owner                if (!focusGrantDelayed) {                    mFocusStack.pop();                    // the entry that was "popped" is the same that was "peeked" above                    fr.release();                }            }            // focus requester might already be somewhere below in the stack, remove it            removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);            final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,                    clientId, afdh, callingPackageName, Binder.getCallingUid(), this);            if (focusGrantDelayed) {                // focusGrantDelayed being true implies we can't reassign focus right now                // which implies the focus stack is not empty.                final int requestResult = pushBelowLockedFocusOwners(nfr);                if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {                    notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);                }                return requestResult;            } else {                // propagate the focus change through the stack                if (!mFocusStack.empty()) {                    propagateFocusLossFromGain_syncAf(focusChangeHint);                }                // push focus requester at the top of the audio focus stack                mFocusStack.push(nfr);            }            notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),                    AudioManager.AUDIOFOCUS_REQUEST_GRANTED);        }//synchronized(mAudioFocusLock)        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;    }
     首次申请audioFocus,因队列为空,故会执行到最后那行代码,返回 AudioManager.AUDIOFOCUS_REQUEST_GRANTED. 表明申请成功。

AudioManager里的申请成功后,会回到播放器apk里的代码处,这整个过程是阻塞式的。

至此,播放器申请audioFocus结束,代码继续向下执行,开始播放音乐。

     如果在本播放apk申请之前已经有其它的播放器正在播放音乐,则本播放器申请audioFocus的时候,上面的主要流程都一样;只是在 MediaFocusControl.java 里的这个requestAudioFocus 函数里运行的顺序有差别;可以具体情况具体分析。

你可能感兴趣的文章
jq实现图片轮播:圆形焦点+左右控制+自动轮播
查看>>
问题:关于贴友分类菜单的实现
查看>>
PHP图像操作:3D图、缩放、旋转、裁剪、添加水印(二)
查看>>
PHP图像操作:3D图、缩放、旋转、裁剪、添加水印(三)
查看>>
document.body、document.documentElement和window获取视窗大小的区别
查看>>
教你利用iframe在网页中显示天气
查看>>
利用Javascript获取当前日期的农历日期
查看>>
利用原生JavaScript获取样式的方式小结
查看>>
PHP制作验证码
查看>>
常用的CSS Hack技术集锦
查看>>
IE 8兼容:X-UA-Compatible的解释
查看>>
关于form.submit()不能提交表单的错误原因
查看>>
初识HTML 5:关于它的三个三
查看>>
Canvas入门(1):绘制矩形、圆、直线、曲线等基本图形
查看>>
Canvas入门(2):图形渐变和图像形变换
查看>>
Canvas入门(3):图像处理和绘制文字
查看>>
《千与千寻》给读者带来了什么?
查看>>
JQuery笔记:JQuery和JavaScript的联系与区别
查看>>
PHP的MySQL扩展:PHP访问MySQL的常用扩展函数
查看>>
PHP实现分页:文本分页和数字分页
查看>>