QigsawBundle之ContentProvider优化

缘由

其实不做优化,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)
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 137,191评论 19赞 139
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 14,589评论 2赞 59
  • 2018年Android 面试题 IT开发仔2018-03-21 15:26:46 在这“金三银四”的季节,我准备...
    王培921223阅读 2,619评论 3赞 24
  • Laravel 学习交流 QQ 群:375462817 本文档前言Laravel 文档写的很好,只是新手看起来会有...
    Leonzai阅读 8,806评论 2赞 12
  • Android P 9浅谈 领导突然吩咐研究一下Android P 9,一脸懵逼对Android O 8都没有普及...
    孙科技阅读 2,284评论 1赞 6

友情链接更多精彩内容