美文网首页
Android加载未安装apk中的资源

Android加载未安装apk中的资源

作者: xiasuhuei321 | 来源:发表于2017-08-07 23:52 被阅读0次

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

    先准备另一个项目

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


    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))
    
        }
    

    效果图:

    少司命

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

    相关文章

      网友评论

          本文标题:Android加载未安装apk中的资源

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