高版本 Android Studio 集成 framework.jar

前言

最近工作需要在项目中集成系统的 framework.jar ,以此来调用系统的定制功能,但是按照之前方案配置之后发现无法调用 framework.jar 中的方法,默认使用的还是 Android SDK 里面的方法。

经过一系列的排查之后终于找到解决方案,做此分享。

集成 framework.jar 方案

导入 framework.jar

首先将 framework.jar 导入项目中,以一个简单 Demo 项目举例,项目中有一个 app 模块,项目结构如下(只列出了关键文件)

bash 复制代码
# 项目结构
Demo
  |---- .idea
  |      |---- modules
  |      |      |---- app
  |      |      |      |---- Demo.app.main.iml
  |---- app
  |      |---- libs
  |      |---- src
  |      |---- build.gradle.kts
  |---- build.gradle.kts
  |---- settings.gradle.kts
  1. framework.jar 文件复制到 Demo/app/libs 路径下;
  2. Demo/app/build.gradle.kts 文件中添加依赖:
kotlin 复制代码
dependencies {
    ...
    // 添加 framework.jar,仅编译
    compileOnly(files("libs/framework.jar"))
}

配置依赖优先级

仅仅在项目中导入 framework.jar 是没有效果的,你会发现之前被标记为 @hide 的接口现在还是不能使用,导入的 framework.jar 中的定制方法还是不能调用,与之前不同的是此时你可以调用 framework.jar 中系统原生不存在的类,这样的效果当然不能让我们满意。

这是由于默认情况下,Android SDK 中的资源优先级是高于手动导入的资源的,因此我们需要手动修改这个优先级,这部分定义在项目根目录下 .idea/modules/app/xxxx.iml 中,所以接下来需要在对应模块的 build.gradle.kts 中添加配置。

Demo 项目中,我们要在 Demo/app/build.gradle.kts 中修改:

kotlin 复制代码
import groovy.namespace.QName
import groovy.util.Node
import groovy.xml.XmlParser
import groovy.xml.XmlUtil
import java.io.FileOutputStream
...
project.tasks.preBuild.get().doLast {
    // 在 preBuild 任务执行完之后处理
    // 定义修改 .iml 文件中 Android SDK 优先级方法
    fun changeSdkOrder(path: String) {
        runCatching {
            val imlFile = File(path)
            with(XmlParser().parse(imlFile)) {
                // 从 .iml 文件中读取 NewModuleRootManager 节点
                val rootManagerComponent = getAt(QName.valueOf("component"))
                    .map { it as Node }
                    .first { it.attribute("name") == "NewModuleRootManager" }
                // 从 NewModuleRootManager 节点中获取 Android SDK 配置节点
                val jdkEntry = rootManagerComponent.getAt(QName.valueOf("orderEntry"))
                    .map { it as Node }
                    .first { it.attribute("type") == "jdk" }
                // 保存节点参数
                val jdkName = jdkEntry.attribute("jdkName")
                val jdkType = jdkEntry.attribute("jdkType")
                println("> Task :${project.name}:preBuild:doLast:changedSdkOrder jdkEntry = $jdkEntry")
                // 从 NewModuleRootManager 节点中移除 Android SDK 配置节点
                rootManagerComponent.remove(jdkEntry)
                // 重新将 Android SDK 配置节点添加到 NewModuleRootManager 的最后
                rootManagerComponent.appendNode(
                    "orderEntry", mapOf(
                        "type" to "jdk",
                        "jdkName" to jdkName,
                        "jdkType" to jdkType
                    )
                )
                // 将新生成的 .iml 写入文件
                XmlUtil.serialize(this, FileOutputStream(imlFile))
            }
        }
    }
    
    // 修改 .iml 文件
    println("> Task :${project.name}:preBuild:doLast:changedSdkOrder")
    changeSdkOrder(rootDir.absolutePath + "/.idea/modules/app/Demo.app.main.iml")
}

方法 changeSdkOrder 的参数为模块对应的 .iml 配置文件路径,可按照自己的项目配置,按需修改。

如此一来,在项目中就可以尽情的使用 framework.jar 中的方法了。

配置引导类路径

经历了上面两个步骤的配置,在开发过程中已经可以正常使用 framework.jar 中的内容的,在本地 Android Studio 中也能正常编译运行,但是如果你们公司配置在服务器编译构建就还是会报错,那就需要在项目根目录的 build.gradle.kts 中添加相关配置。

Demo 项目中,我们要在 Demo/build.gradle.kts 中修改:

kotlin 复制代码
...
allprojects {
    beforeEvaluate {
        // framework.jar 路径
        val path = rootDir.absolutePath + "/app/libs/framework.jar"
        tasks.withType<JavaCompile> {
            // 低版本 gradle 的方案
            options.compilerArgs.add("-Xbootclasspath/p:$path")
            // 高版本 gradle 的方案
            val newFileList = mutableListOf<File>()
            newFileList.add(File(path))
            options.bootstrapClasspath?.files?.let { oldFileList ->
                newFileList.addAll(oldFileList)
            }
            options.bootstrapClasspath = files(*newFileList.toTypedArray())
        }
    }
}

通过上面的配置,我们将 framework.jar 添加到引导类路径中,无论是在本地还是在服务器构建,就都不会出现问题了。

高版本 Android Studio 适配

讲了这么多,终于到了我们这篇文章的重点,就是高版本 Android Stuido (这里特指 3.6.3之后的版本)的适配。

作者这边使用的 Android Studio 版本是 Iguana,按照上面的步骤配置完之后发现并没有起作用,具体现象就是导入的 framework.jar 中的定制方法不能调用,只可以调用 framework.jar 中系统原生不存在的类,那不就是 依赖优先级 的配置失效了吗,打开 Demo/.idea/modules/app/Demo.app.main.iml 文件,发现里面只剩简单的几行配置,依赖优先级 相关的几个节点都没有了,那该怎么配置?

经过多次 Google百度Bing 无果,能找到的都是已知的配置,最终找到了 Android Studio 中的一个配置项,路径在 File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle 中,有一个 Generate *.iml files for modules imported from Gradle,把这个选项勾选上,restart Android Studio.iml 文件中的配置就恢复了。

经过多个版本的多次尝试,最终发现:在 Android Studio 3.6.3 版本及以前,Generate *.iml files for modules imported from Gradle 选项默认是勾选上的,而在 3.6.3 之后的版本,该选项默认是没有勾选的。经历了九九八十一难,终于解决了这个奇葩的问题,感谢各位的观看!

文章作者: WangJie0822

文章链接: www.wangjie0822.top/posts/d0b7d...

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 WangJie0822

相关推荐
wk灬丨35 分钟前
Android Choreographer 监控应用 FPS
android·kotlin
大胃粥1 小时前
Android U WMS : Activity 冷启动(2) 添加启动窗口
android
魏大橙2 小时前
长亭WAF绕过测试
android·运维·服务器
志尊宝2 小时前
Android 中使用高德地图实现根据经纬度信息画出轨迹、设置缩放倍数并定位到轨迹路线的方法
android
吾爱星辰2 小时前
Kotlin while 和 for 循环(九)
android·开发语言·kotlin
我命由我123452 小时前
Kotlin 极简小抄 P3(函数、函数赋值给变量)
android·开发语言·java-ee·kotlin·android studio·学习方法·android-studio
大风起兮云飞扬丶3 小时前
Android——内部/外部存储
android
niurenwo4 小时前
Android深入理解包管理--记录存储模块
android
文 丰5 小时前
【Android Studio】使用雷电模拟器调试
android·ide·android studio
蓝影铁哥6 小时前
SpringBoot3核心特性-核心原理
android·java·数据库·spring boot