Android加载未安装apk中的资源

上篇介绍了如何从dex中加载类,这篇尝试了一下从apk中加载资源,用的同样是DexClassLoader。同样还是那个kotlin项目,简单的尝试从另一个apk的drawable中加载一张图片,个人感觉还是挺麻烦的。

先准备另一个项目

新建另一个项目,这个项目只在drawable下放了一张名为ssm的图片。然后生成一个debug的apk包。将这个apk拷贝到本项目的assets下(只是为了方便,也可以从远程获取这个apk)。


Android加载未安装apk中的资源_第1张图片
debug apk

我们的目标就是通过这个debug的apk来加载这张图片,这里我将这个apk的名字改为了plugin1.apk。

简单的代码

这里不得不说一下,kotlin对java的兼容做的真的不错,这里的反射本来还有点担心该怎么做,后来发现比较容易的就实现了。只不过语法上有一些小小的不同。在Java中通过类名.class就可以访问该类的Class对象,而在Kotlin中则需要类名::class.java,可以访问到java的Class对象。

    fun dynamicLoadApk() {
        val pm = packageManager
        // 在应用安装目录下创建一个名为plugin的文件夹目录
        // 我进了程序目录看了一下,叫app_plugin
        val optDir = getDir("plugin", Context.MODE_PRIVATE)
        // 生成输出文件
        val desFile = File(optDir.path + File.separator + "plugin1.apk")
        println(desFile.path)
        println(desFile.absolutePath)
        // 如果不存在,将assets下的plugin1.apk复制到输出文件中
        if (!desFile.exists()) {
            desFile.createNewFile()
            copyFiles("plugin1.apk", desFile)
        }

        // plugin1.apk 获取包名
        val pkInfo = pm.getPackageArchiveInfo(desFile.path,
                PackageManager.GET_ACTIVITIES)
        val packageName = pkInfo.applicationInfo.packageName

        // 访问AssetManager的Class对象,生成AssetManager实例对象
        val assetManager = AssetManager::class.java.newInstance()
        // 通过反射拿到隐藏方法
        val addAssetPath = assetManager.javaClass.getMethod("addAssetPath", String::class.java)

        val loader = DexClassLoader(optDir.path + File.separator +
                "plugin1.apk", optDir.path, null,
                ClassLoader.getSystemClassLoader())


        addAssetPath.invoke(assetManager, desFile.path)
        val superRes = resources
        val mResources = Resources(assetManager, superRes.displayMetrics, superRes.configuration)

        val clz = loader.loadClass("$packageName.R\$drawable")
        val field = clz.getDeclaredField("ssm")
        val resId = field.getInt(R.id::class)
        println("resId: $resId")
        val iv_img = findViewById(R.id.iv_img) as ImageView
        iv_img.setImageDrawable(mResources.getDrawable(resId))

    }

效果图:

Android加载未安装apk中的资源_第2张图片
少司命

的确可以加载成功。但是到这,这两篇只能算是hello world,比起普通的资源加载,更令人向往的是启动各个未安装的apk中的Activity和各种Service。不过比起到现在为止的直接反射暴力新建对象,Activity作为系统的组件,需要系统来初始化,来调用各个生命周期方法。后续就是就是要关注一下如何调用这些资源了。

你可能感兴趣的:(Android加载未安装apk中的资源)