BUG:记MediaBrowserService的onLoadChildren不执行

注:安卓设备7.0  support-v4-25.3.1

想写一个音乐播放器,看google15年推荐的用MediaBrowserService 和 MediaBrowser,所以想着用一下对我而言新的技术,写完了才发现有坑啊。


bug描述:在音乐列表页启动之前,按下home键切换到后台,再切换回来,启动列表页,发现没数据啊!MediaBrowserService的onLoadChildren根本没执行。


原因:

在Activity进入后台的时候,在onStop时MediaBrowser.disconnect(),调用远程的unregisterCallbacks,会将MediaBrowserServiceCompat.mConnection中的ConnectionRecord清除掉。

Activity回到前台,在onStart中判断时候连接,进行MediaBrowser.connect(),而这个时候,是不会像第一次启动时候,调用registerCallbacks的,因为MediaBrowser.onServiceConnected()中的bundle返回为空了,所以无法在
MediaBrowserImplApi21::onConnected()的时候registerCallbackMessenger,就不能在Service端中添加ConnectionRecord,MediaBrowser.addSubscription的时候,onLoadChildren前,查不到相应的ConnectionRecord,所以就无法onLoadChildren()


我的解决办法:

我是参考谷歌的Demo写的(地址),在我写博客的这个时间<2017年05月22日11:59:29>之前,它的demo也是存在这个问题的,所以我建议在onDesytroy中,进行MediaBrowser.disconnect(),在onCreate中,进行MediaBrowser.connect()。



2017年05月22日15:00:33 更新:

在代码里溜达了一圈,原来是在onGetRoot方法中,返回的了new BrowserRoot("***", null),第二个参数不能为null,这里要返回服务端的MessengerImpl,MediaBrowserCompat.onConnected()这里要拿到第二个参数,取出serviceBinder不为空才可以注册成功,注册成功才能回调onLoadChildren(),但是要怎么拿到MessengerImpl呢?除非你能拿到ServiceHandler mHandler = new ServiceHandler() 这个mHandler对象,所以还是按照上面的办法来做吧。


新的问题:第二个参数为null时,为什么第一次启动的时候可以注册成功呢?

第一次创建MediaBrowser的时候,mRootHint中有个参数{EXTRA_CLIENT_VERSION,1},

这个参数在MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot中判断EXTRA_CLIENT_VERSION不为0时候,会在bundle中添加{EXTRA_MESSENGER_BINDER,iBinder},

public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
                String clientPackageName, int clientUid, Bundle rootHints) {

            Bundle rootExtras = null;
            //第一次进来的时候EXTRA_CLIENT_VERSION为1,
            //所以rootExtras中有mMessenger.getBinder()这样一个binder
            //从后台切换回来后,rootHints是一个空的bundle,还没找到为空的原因,所以这里不会添加binder
            if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
                rootHints.remove(EXTRA_CLIENT_VERSION);
                mMessenger = new Messenger(mHandler);
                rootExtras = new Bundle();
                rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
                BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
            }
            //这个回调的是你自己实现的onGetRoot方法,很多时候可能你返回的不是null
            BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
                    clientPackageName, clientUid, rootHints);
            if (root == null) {
                return null;
            }
            //第一次进来的时候rootExtras不为null,所以第一次返回了带有binder的BrowserRoot
            //在MediaBrowser.onServiceConnected()中接受到赋值给mExtras了,所以可以拿到binder,
            //在MediaBrowserCompat.onConnected()中通过MediaBrower拿到了binder
            //binder不为空,才能new Messenger(binder),通发送注册CallBack的消息
            //从后台切换回来后,rootExtras为null,就返回了你自己实现onGetRoot中的内容
            if (rootExtras == null) {
                rootExtras = root.getExtras();
            } else if (root.getExtras() != null) {
                rootExtras.putAll(root.getExtras());
            }
            return new MediaBrowserServiceCompatApi21.BrowserRoot(
                    root.getRootId(), rootExtras);
        }

2017年05月23日11:01:15

我可能发现了这个包的一个bug,为什么从后台切换回来后rootHint就为空了呢?

MediaBrowser.java

private class MediaServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(final ComponentName name, final IBinder binder) {
            postOrRun(new Runnable() {
                @Override
                public void run() {
                    //注意mRootHints的地址
                    mServiceBinder.connect(mContext.getPackageName(), mRootHints,
                            mServiceCallbacks);
                }
            }
        });
    }


MediaBrowserService.java

 private class ServiceBinder extends IMediaBrowserService.Stub {
        @Override
        public void connect(final String pkg, final Bundle rootHints,
                final IMediaBrowserServiceCallbacks callbacks) {
            mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //这里的地址是和上面穿过来的地址是一样的
                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);

                    }
           
        }
    }


MediaBrowserServiceCompat.java

public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
                String clientPackageName, int clientUid, Bundle rootHints) {
        if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
                //这里SeriveBinder是MediaBrowserServiceCompatApi21的内部类,
                //所以这个rootHints的地址相当于是MediaBrowser里的mRootHint
                //这里相当于把MediaBrowser里的mRootHint的值给清除了
                //所以从后台切回来你根本拿不到ServiceHandler的Messager
                rootHints.remove(EXTRA_CLIENT_VERSION);
            }
}



有其他看法的同学,欢迎留言讨论

2017年05月26日08:10:05

https://issuetracker.google.com/issues/37122290

这个问题确实存在,但是看2017年March月关闭了,但是没解决这个问题

看官方的26.0.0并未提到该问题的修复,所以目前我的App的解决办法connect写在onCreate里,disconnect写在onDestroy里,因为我设计的只有一个MainActiviy。或者你再次connect的时候,new一个新的MediaBrowserCompat,再或者你可以把Service端放在另一个进程中

BUG:记MediaBrowserService的onLoadChildren不执行_第1张图片

2018年01月11日17:31:24:

使用API 26 和对应的SupportV4 26.0.2以上版本可以解决

代码里加了对mCurConnection这样一个东西。


你可能感兴趣的:(Android,MediaBrowser,音乐,播放器)