RePlugin集成AndroidAutoSize

1,474 阅读5分钟

一、屏幕适配

你可能会问 "都 2021 年了还折腾屏幕适配?直接用 dp 单位适配不好吗?", 确实,如果公司的 UI 设计师、产品经理以及老板,能明白为什么一套 UI 在不同设备上显现出来的一些差异并不是 bug 时,开发者直接用 dp 单位配合一些布局技巧来进行适配是可以的,但是,实现多数情况下并不是如此美好,他们更希望看到的是 UI 在不同屏幕上,仅仅只是缩放的区别,为了满足这种要求,应用第三方屏幕适配方案就非常有必要了。根据在网上查到的比较好的原生屏幕适配方案有两种:

  • smallestWidth 限定符适配方案
  • 今日头条屏幕适配方案

两者的原理,以及优缺点这里不废话,有兴趣的可通过下方链接了解:

考虑到 apk 体积包大小问题,最终还是选择了 AndroidAutoSize,同时项目中有使用到插件化框架 RePlugin,需要在插件中集成 AndroidAutoSize,然而网上又几乎没有相关的集成踩坑资料,于是便有了本篇文章~

二、问题与解决方案

AndroidAutoSize 的使用说明及已知问题汇总请访问以下链接查看:

按作者的集成步骤,在 app 作为单品运行时是没有任何问题的,但是,如果你项目中使用了 RePlugin,一旦 app 作为插件运行中,你就会发现,一点用都没有!!!

注意:本文基于关闭常驻进程的前提完成的论证及测试,即:persistentEnable = false

1、框架初始化

不可否认,AndroidAutoSize 的框架设计很优秀,利用 ContentProvider 实现框架自动初始化工作,然而,这种"黑科技"在个别情况下不好使,比如有些魔改 ROM,还有就是作为 RePlugin 插件的时候,所以,AndroidAutoSize 没起作用的一方面原因是框架未能正常初始化,插件 AndroidManifest.xml 中声明的 ContentProvider 并不会自动被执行创建,也就是 InitProvider 没被执行到,好在该库提供了手动初始化的方式:

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        // 手动直接执行 AutoSizeConfig#init()
        AutoSize.checkAndInit(this)

        // 通过启动InitProvider,再由InitProvider执行 AutoSizeConfig#init()
        // AutoSize.initCompatMultiProcess(this)
    }
}

AutoSize.checkAndInit(this) 亲测有效,AutoSize.initCompatMultiProcess(this) 没有测试过,理论上也是可以的,但大可不必。

通过上面的代码就可以解决 AndroidAutoSize 的初始化问题。另外,如果你担心 app 作为单品时,AutoSizeConfig#init() 会被 AutoSize.checkAndInit(this)InitProvider 执行多次的话,你可以选择把 InitProvider 移除掉:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        ...
        android:name=".MyApplication">

        <provider
            tools:node="remove"
            android:name="me.jessyan.autosize.InitProvider"
            android:exported="false"
            android:multiprocess="true"
            android:authorities="${your application id}.autosize-init-provider" />
    </application>

</manifest>

关键点是 tools:node="remove",可以把 aar 中的 AndroidManifest.xml 声明好的组件移除。此处的 ${your application id} 需要根据你当前 app 的 appid 来修改,不知道怎么改的话,可以先正常打个包,再反编译看看 AndroidManifest.xml 中原来的 InitProvider 声明是怎样的就明白了。

其实并不需要担心 AutoSizeConfig#init() 会被执行多次的问题,框架肯定有做好防护措施的,但如果你很严谨,不想有意外情况发生的话,强烈建议把 InitProvider 移除,这样的话,不管是单品还是插件,初始化逻辑都将是一样的。

2、启动屏幕适配功能

解决完框架初始化的问题,现在 AndroidAutoSize 还是无法起作用的。通过 AndroidAutoSize 源码可以得知,框架会给 Application 注册一个 ActivityLifecycleCallbacks,用于执行各个 Activity 初始化时的适配工作,然后,当 app 作为插件时这个 ActivityLifecycleCallbacks 是不会回调的-_-

  • 原因:插件的 Application 无法正常注册 ActivityLifecycleCallbacks
  • 解决方案:用宿主的 Application 注册 ActivityLifecycleCallbacks

怎么用宿主的 Application 注册 ActivityLifecycleCallbacks 呢?Easy,在初始化 AndroidAutoSize 的时候用宿主的 Application 即可:

// if (RePlugin.getHostContext() != null) { // 插件
//     AutoSize.checkAndInit(RePlugin.getHostContext().applicationContext as Application)
// } else { // 单品
//     AutoSize.checkAndInit(this)
// }

// 简洁写法:
AutoSize.checkAndInit(RePlugin.getHostContext()?.applicationContext as? Application ?: this)

好了,到这里 AndroidAutoSize 就起作用了。

这个 ActivityLifecycleCallbacks (也就是 ActivityLifecycleCallbacksImpl) 相当重要,它生效了, CancelAdaptCustomAdapt 才能正常使用。

3、指定设计图尺寸

现在框架初始化,以及屏幕适配功能都已经没有问题了,接下来就是配置工作,默认情况下,AndroidAutoSize 会读取开发者在 AndroidManifest.xml 中预先填写好的 meta 设计图尺寸,用于计算 DisplayMetrics#density,然而当 app 作为插件时,你会发现这个配置也是无效的。

  • 原因:框架中通过 context 拿到的是宿主的 AndroidManifest.xml 信息,因为上面 AutoSize#checkAndInit() 传入的是宿主的 Application
  • 解决方案:由于 AutoSizeConfig#getMetaData(final Context context) 是私有方法,所以,你有 2 种选择:
    • 反射调用 AutoSizeConfig#getMetaData() 并传入插件的 Application
    • 改为代码配置的方式,给 AutoSizeConfig 直接指定设计图尺寸

别跟我说什么在宿主的 AndroidManifest.xml 中配置设计图尺寸,太 low 了-_-

怎么简单怎么来,这里就通过代码方式给 AutoSizeConfig 直接指定设计图尺寸:

AutoSize.checkAndInit(RePlugin.getHostContext()?.applicationContext as? Application ?: this)

// 注意:一定要在框架初始化之后再进行配置,否则框架内部拿不到正确的设计图尺寸(会被 AutoSizeConfig#init() 重置)
AutoSizeConfig.getInstance()
    // 手动指定设计图尺寸,不读取清单文件
    .setDesignWidthInDp(1280)
    .setDesignHeightInDp(720)
    // 设置使用冷门尺寸单位作为副单位
    .unitsManager
    .setSupportDP(false)
    .setSupportSP(false)
    .setSupportSubunits(Subunits.MM)

至此,AndroidAutoSize 无论是单品还是插件都能正常运作了,Nice~

三、其他

这里就已经和 RePlugin 的坑没有关系了,只是记录一些 AndroidAutoSize 的使用问题。

1、重写 Activity#getResources() 导致 CancelAdaptCustomAdapt 失效

原因是在 getResources()方法中没有判断当前 Activity 是否有实现 CancelAdaptCustomAdapt,一股脑全部适配导致的,只需要做个判断就好了:

abstract class BaseActivity : AppCompatActivity() {

    ...

    override fun getResources(): Resources {
        when (this) {
            is CancelAdapt -> {
                AutoSizeCompat.cancelAdapt(super.getResources())
            }
            is CustomAdapt -> {
                AutoSizeCompat.autoConvertDensityOfCustomAdapt(super.getResources(), this)
            }
            else -> {
                //需要升级到 v1.1.2 及以上版本才能使用 AutoSizeCompat
                AutoSizeCompat.autoConvertDensityOfGlobal((super.getResources())) //如果没有自定义需求用这个方法
                // AutoSizeCompat.autoConvertDensity((super.getResources()), 667f, false)//如果有自定义需求就用这个方法
            }
        }
        return super.getResources()
    }
}