美文网首页
QigsawBundle之ContentProvider优化

QigsawBundle之ContentProvider优化

作者: DonaldDu | 来源:发表于2022-03-07 18:05 被阅读0次

    缘由

    其实不做优化,DynamicProviderSwitch也够用了。主要是想简化编译流程,提高编译速度,且尽量和官方流程一样。旧方案在api>=28的安卓版本上使用了隐藏api,新版本中可能变更。这次优化中使用api28新增的AppComponentFactory来替代隐藏api,以提升稳定性。api>=28时,使用的全是公开api。

    Provider在应用启动时就会被调用,Dynamic中的Provider第一次启动是找不到的,应用会直接报错,无法启动。Qigsaw为每个DynamicProvider生成了一个代理类xxxContentProviderProxy(或叫装饰类),找不到原DynamicProvider时,就调用代理类。

    QigsawBundle不生成任何装饰类,而是通过工具DynamicProviderSwitch自动把DynamicProvider设置为关闭状态(android:enabled="false")。启动应用时,再读取manifest中哪些DynamicProvider类存在,如果存在则启动Provider,不存在则忽略。Split安装后重试未启动成功的DynamicProvider。

    DynamicProviderSwitch:2.0.0 以前是在编译期间修改编译输出的manifest文件来禁用DynamicProvider。2.0.0以后是在运行时,通过代码禁用DynamicProvider的。

    干货

    很早以前就发现一个工具HookUtil,觉得可以用来优化DynamicProviderSwitch,最近有时间就尝试并完成了,主要原理如下。

    api<28

    当api<28时,通过HookUtil在启动Provider前,把DynamicProvider禁用并从启动列表移除。

    主要代码如下

        /**
         * api>=28 -> HookProviderFactory & ContentProviderProxy,
         * else disable Provider.
         * must call in Application#attachBaseContext
         * */
        fun compatDynamicProvider(base: Context) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
                try {
                    attachContext()
                    disableDynamicProviders(base)
                    initProvider(base)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
    

    api>=28

    当api>=28时,通过HookProviderFactory替换不存在的类为ContentProviderProxy(代码来自Qigsaw),然后通过ContentProviderProxy代理Provider的所有行为。每次触发事件时,尝试加载真实类。如果加载成功,以后则用真实类完成请求。这个逻辑和Qigsaw基本一致,但Qigsaw是在编译期间生成代理类,QigsawBundle则是用DynamicProviderSwitch在运行时替换成代理类。

    @RequiresApi(Build.VERSION_CODES.P)
    open class HookProviderFactory : AppComponentFactory() {
        private val TAG = "ProviderSwitch"
        override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
            if (BuildConfig.DEBUG) Log.i(TAG, "instantiateProviderCompat $className")
            val dynamicProvider = try {
                Class.forName(className).name.isEmpty()
            } catch (e: ClassNotFoundException) {
                true
            }
            return if (dynamicProvider) {
                if (BuildConfig.DEBUG) Log.i(TAG, "ContentProviderProxy $className")
                super.instantiateProviderCompat(cl, ContentProviderProxy::class.java.name)
            } else {
                super.instantiateProviderCompat(cl, className)
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:QigsawBundle之ContentProvider优化

          本文链接:https://www.haomeiwen.com/subject/cppnrrtx.html