高版本 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

相关推荐
雨白9 分钟前
Android 自定义 View:从绘制基础到实战仪表盘与饼图
android
jiunian_cn29 分钟前
【Linux】线程
android·linux·运维·c语言·c++·后端
Frank_HarmonyOS9 小时前
Android MVVM(Model-View-ViewModel)架构
android·架构
新子y13 小时前
【操作记录】我的 MNN Android LLM 编译学习笔记记录(一)
android·学习·mnn
lincats14 小时前
一步一步学习使用FireMonkey动画(1) 使用动画组件为窗体添加动态效果
android·ide·delphi·livebindings·delphi 12.3·firemonkey
想想吴15 小时前
Android.bp 基础
android·安卓·android.bp
bianshaopeng21 小时前
Android studio gradle 下载不下来
ide·android studio
写点啥呢1 天前
Android为ijkplayer设置音频发音类型usage
android·音视频·usage·mediaplayer·jikplayer
coder_pig1 天前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
死就死在补习班1 天前
Android系统源码分析Input - InputReader读取事件
android