BUG:记MediaBrowserService的onLoadChildren不执行

来源:互联网 发布:宝宝学画画软件 编辑:程序博客网 时间:2024/06/09 15:33

注:安卓设备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.javaprivate 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.javapublic 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端放在另一个进程中

原创粉丝点击