0%

第12章:ContentProvider的插件化解决方案

ContentProvider基本概念

ContentProvider 就是一个SQLite 数据库,数据提供方A和数据使用方B是通过匿名共享内存来传输数据的。B告诉A,“你把数据写在这个内存地址上”;B准备好数据,写到A要求的内存地址上,A就可以直接使用这些数据了。当数据量非常大的时候,这个数据传递速度是非常快的。

并不是所有数据传递都需要ContentProvider,比如,Activity 跳转时,数据的传递就用的 Binder,一般来说,传输的数据量不超过 1 M 时,使用Binder;否则,此时需要ContentProvider 。

ContentProvider 插件化

前面介绍了 BroadCastReceiver 的插件化解决方案,即把插件中的静态广播都转换为动态广播,然后手动注册到宿主App的广播中。

其实,ContentProvider 也能这么做,这时候不叫“注册”,而叫“安装”。安装当前Apk中所有的ContentProvider 的方法位于 ActivityThread 的 installContentProviders方法中:

1
//代码略

我么你只需要手动执行这个方法,把插件中的ContentProvider 集合作为第二个参数填进去即可。如此一来,我们得到了ContentProvider 插件化的解决方案:

  1. 沿用Activity插件化的第二种方案,将宿主App和插件App的dex合并到一起
  2. 借助PackageParse的parsePackage方法,读取插件中的ContentProvider信息,然后把得到的Package对象转换为我们需要的 ProviderInfo类型对象
  3. 将ContentProvider 的packageName 设置为当前apk的packageName,之后把插件中的 ContentProvider 放入宿主中
  4. 通过反射执行 ActivityThread 的 installContentProviders 方法,把ContentProvider 作为插件的参数,相当于把插件 ContentProvider “安装” 到宿主App中

执行这段Hook代码的时机

ContentProvider 这个组件,往往是提供给外界使用的,如果插件中的ContentProvider 还没安装到宿主App中,第三方就来调用了,那就要等很久了,所以安装插件 ContentProvider 的过程越早越好。App安装自身的 ContentProvider 是在ActivityThread 执行 installContentProviders 方法中,这个方法比Application 的onCreate 要早,但是会晚于 Application 的 attachBaseContent 方法,所以,我们可以在 attachBaseContent 方法中,手动执行 ActivityThread 的 installContentProviders

ContentProvider 的转发机制

让外界App直接调用当前App的插件里定义的ContentProvider ,并不是理想的解决方案。最好的是在宿主App中定义一个 StubContentProvider 作为中转,让外界调用当前App的 StubContentProvider ,然后在 StubContentProvider 中再调用插件里的 ContentProvider:

ContentProvider插件化分发思想

ContentProvider 插件化的精髓在于分发,外界使用 App 提供的 ContentProviderA 时,只知道发送给一个宿主AndroidManifest 中声明锅的 ContentProviderA,而受到请求后,再做二次转发。

谢谢你的鼓励