加载外部 apk 中的资源

文章目录

原理

apk 中的资源放在 resources.arsc、assets 中,它们都要靠 AssetManager 来访问,我们又通过 Resources 来调用 AssetManager。
在这里插入图片描述

要访问外部 apk 中的资源,就要通过该 apk 的 resources.arsc,创建一个新的 AssetManager 和一个新的 Resources。

实例

下面的例子,会在宿主 apk 中访问外部 apk 中的一个 String 资源。

在 demo 中加入一个 String 资源,打包 simple.apk。

<string name="hello_str">hello plugin</string>

通过 AS 查看 simple.apk,看到 R.string.hello_str 的值是 0x7f0b0028
在这里插入图片描述
代码:

class ResourcesActivity : AppCompatActivity() {

    companion object {
        const val PLUGIN_APK_NAME = "simple.apk"
    }

    var pluginApkPath: String = ""
    var mResources: Resources? = null

    override fun attachBaseContext(newBase: Context?) {
        super.attachBaseContext(newBase)

        // 将 simple.apk 复制到 /data/data/packageName/files
        val apkFile = newBase?.getFileStreamPath(PLUGIN_APK_NAME)
        pluginApkPath = apkFile?.absolutePath ?: ""
        FileUtil.copyFileFromAssets(newBase, PLUGIN_APK_NAME, apkFile, false)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_resources)

        // 创建新的 Resources 对象
        loadResources()
        // 7f0b0028,即 simple.apk 的 resources.arsc 中 R.string.hello_str 的 id 值
        val s = mResources?.getString("7f0b0028".toInt(16))
        tv_plugin_res_string.text = s
    }

    private fun loadResources() {
        // 创建新的 AssetManager 对象
        val mAssetManager = AssetManager::class.java.newInstance()
        // 将 simple.apk 的路径传入 AssetManager
        MethodUtils.invokeMethod(mAssetManager, "addAssetPath", pluginApkPath)
        // 通过 AssetManager 创建 Resources
        mResources = Resources(mAssetManager, resources.displayMetrics, resources.configuration)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章