在 Android Studio 中公开隐藏的 API android L (SDK 21)
Exposing hidden APIs android L (SDK 21) in Android Studio
我已经成功地从 android 模拟器中提取了 class 文件,并将它们替换到驻留在 \platforms\android-21\android.jar 中的 android.jar 文件中,但我仍然无法访问隐藏的方法。我之前使用过 android 19,但为了让我的构建保持最新。我知道我可以使用反射,但是在 Android Studio 中原生支持它会容易得多。
使用 JD-GUI 我可以确认 classes 和方法在 android.jar 文件中,但 Android Studio "cannot resolve symbol" 在每个文件中。我主要在这里感到困惑。谢谢大家。
编辑:我确实有回购设置,所以如果可以从源代码编译 android.jar 文件,我不介意这样做。
文件 > 使缓存无效并重新启动 > 使缓存无效并重新启动
我知道有些事情很奇怪。我知道方法就在罐子里,我知道它是 Studio 的东西。它在这里缓存方法:)
按照我在第一行发布的内容修复它!很高兴!希望其他人受益!
您不必替换 \platforms\android-21\android.jar.
它会破坏 SDK,您可能无法将这些更改提交到 git。
下面的方法可能更简单:
见How do I build the Android SDK with hidden and internal APIs available?得到framework_all.jar
将framework_all.jar放在项目中除了应用程序之外的system_libraries。
改变app/build.gradle喜欢
依赖关系{
编译文件树(目录:'libs',包括:['<em>.jar'])
提供的文件树(目录:'../system_libraries',包括:['</em>.jar'])
}
AS 会给出类似 "Gradle files have changed since .... Sync Now" 的提示,点击立即同步。
转到项目视图,您将在外部库中看到 platform_all.jar。
每次您更改 system_libraries 下的任何 .jar 时,您需要在 app/build.gradle 中做一些更改并重新同步它。
- 在../app下创建文件夹,命名为'systemlibs'
- 将您的图书馆复制到 'systemlibs'
下
- 在 app/build.gradle 依赖项中添加提供的文件 ('systemlibs/android.jar')
- 同步gradle并构建
dependencies {
provided files('systemlibs/android.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
}
这是一个替代方案,它根本不需要构建整个 android SDK JAR 或修改您的 android SDK。如果你有一个包含一些 @hide
android SDK 类 and/or 方法的 JAR,你可以将这个 hunk 添加到你的标准 android 项目 build.gradle 文件:
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xbootclasspath/p:<FULL_PATH_TO_CUSTOM_ANDROID_API>.jar"
}
}
}
这会将您的 JAR 添加到引导类路径中,其中包含来自所选 SDK 的 android.jar。 java 编译器在编译时更愿意在前置引导类路径中使用 类,从而覆盖 SDK 中的 android.jar。
见http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html
缺点是 IntelliJ/Android Studio 似乎无法在更高级别理解这一点,因此方法在编辑器中显示为红色,但构建工作正常。实际上,您可以将其与 provided
解决方案结合使用,以至少获得额外的 类 在编辑器中显示为有效,但暴露的 @hide
方法对于 IDE 仍然未知。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'], 'exclude': ['<PATH_TO_CUSTOM_ANDROID_API>.jar'])
provided files('libs/<PATH_TO_CUSTOM_ANDROID_API>.jar')
}
我使用它的时间还不够长,无法找到如何在 compilerArgs 中引用相对路径,但这应该很简单。
有一段时间我使用了 Xbootclasspath
技巧,但 Android Studio 不太喜欢它(突出显示已损坏),如果您尝试访问隐藏的,它根本不起作用API 在 SDK 级别上发生了变化(这些 API 是隐藏的,因此它们不稳定)。
最终我找到了一个解决方案,涉及创建在我的 Android 项目中使用的纯 java 库。我称此技巧为 "platform-unhidden" 又名 P-U。主要的烦恼是有更多的样板代码和库结构复杂。
如果您需要支持两个级别(比方说 API 级别 25 及更低,以及 API 级别 26 及更高),那么您将需要 5 个 java 库。 "stubs" 库包含从 AOSP 复制的 Android 平台代码,但像 SDK 中的 android.jar 一样,所有内容都被删除了,但带有您想要公开的隐藏方法。
"wrap" 库使用存根库并公开具有唯一名称的 methods/classes。 P-U 库根据检测到的 SDK 级别选择合适的包装库 Build.SDK_INT
.
视觉上看起来像这样:
+---------+ +----------+
+--api-->+ wrap-25 +--compile-only-->+ stubs-25 +--+
+-------+ | +---------+ +----------+ | +---------+
| +---+ +-->+ |
| P-U | | android |
| +---+ +-->+ |
+-------+ | +---------+ +----------+ | +---------+
+--api-->+ wrap-26 +--compile-only-->+ stubs-26 +--+
+---------+ +----------+
设置 java 库需要创建共享 gradle 文件,如下所示:
平台-shared.gradle:
// All the platform-* projects apply this gradle file
apply plugin: 'java'
apply plugin: 'java-library'
sourceCompatibility = 1.8
apply from: file("${androidBuild}/versions.gradle")
// ANDROID_HOME can override ANDROID_SDK_ROOT
// see https://developer.android.com/studio/command-line/variables
def androidHome = System.getenv('ANDROID_HOME')
def androidSdkRoot = System.getenv('ANDROID_SDK_ROOT')
if (androidHome?.trim() && new File(androidHome).canRead()) {
androidSdkRoot = androidHome
}
if (!androidSdkRoot?.trim() || !new File(androidSdkRoot).canRead()) {
def props = new Properties()
def file = new File("${rootDir}/local.properties")
if (file.exists()) {
file.withInputStream { props.load(it) }
if (props.getProperty("sdk.dir")) {
androidSdkRoot = props.getProperty("sdk.dir")
}
}
else {
throw new GradleException('Android SDK root not usable')
}
}
repositories {
// Need an Android SDK jar to compile against even though this is a pure java project
repositories {
flatDir {
dirs "${androidSdkRoot}/platforms/android-${COMPILE_SDK_VERSION}"
}
}
mavenCentral()
}
// Make CI happy by implementing these tasks
task assembleRelease {
dependsOn 'jar'
}
task assembleDebug {
dependsOn 'jar'
}
task assembleAndroidTest {
// Ignored
}
task lintRelease {
// Ignored
}
task lintDebug {
// Ignored
}
task testReleaseUnitTest {
// Ignored
}
task testDebugUnitTest {
// Ignored
}
然后你需要 gradle 每个库的文件,就像这样(没有显示 26,它看起来和 25 一样)。根据需要添加更多级别。
存根-25.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
api name: "android"
}
wrap-25.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
compileOnly project(':platform-stubs-25')
}
平台-unhidden.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
compileOnly project(':platform-stubs-25')
compileOnly name: "android"
api project(':platform-wrap-25')
api project(':platform-wrap-26')
}
我已经成功地从 android 模拟器中提取了 class 文件,并将它们替换到驻留在 \platforms\android-21\android.jar 中的 android.jar 文件中,但我仍然无法访问隐藏的方法。我之前使用过 android 19,但为了让我的构建保持最新。我知道我可以使用反射,但是在 Android Studio 中原生支持它会容易得多。
使用 JD-GUI 我可以确认 classes 和方法在 android.jar 文件中,但 Android Studio "cannot resolve symbol" 在每个文件中。我主要在这里感到困惑。谢谢大家。
编辑:我确实有回购设置,所以如果可以从源代码编译 android.jar 文件,我不介意这样做。
文件 > 使缓存无效并重新启动 > 使缓存无效并重新启动
我知道有些事情很奇怪。我知道方法就在罐子里,我知道它是 Studio 的东西。它在这里缓存方法:)
按照我在第一行发布的内容修复它!很高兴!希望其他人受益!
您不必替换 \platforms\android-21\android.jar.
它会破坏 SDK,您可能无法将这些更改提交到 git。
下面的方法可能更简单:
见How do I build the Android SDK with hidden and internal APIs available?得到framework_all.jar
将framework_all.jar放在项目中除了应用程序之外的system_libraries。
改变app/build.gradle喜欢
依赖关系{ 编译文件树(目录:'libs',包括:['<em>.jar']) 提供的文件树(目录:'../system_libraries',包括:['</em>.jar']) }
AS 会给出类似 "Gradle files have changed since .... Sync Now" 的提示,点击立即同步。
转到项目视图,您将在外部库中看到 platform_all.jar。
每次您更改 system_libraries 下的任何 .jar 时,您需要在 app/build.gradle 中做一些更改并重新同步它。
- 在../app下创建文件夹,命名为'systemlibs'
- 将您的图书馆复制到 'systemlibs' 下
- 在 app/build.gradle 依赖项中添加提供的文件 ('systemlibs/android.jar')
- 同步gradle并构建
dependencies {
provided files('systemlibs/android.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
}
这是一个替代方案,它根本不需要构建整个 android SDK JAR 或修改您的 android SDK。如果你有一个包含一些 @hide
android SDK 类 and/or 方法的 JAR,你可以将这个 hunk 添加到你的标准 android 项目 build.gradle 文件:
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xbootclasspath/p:<FULL_PATH_TO_CUSTOM_ANDROID_API>.jar"
}
}
}
这会将您的 JAR 添加到引导类路径中,其中包含来自所选 SDK 的 android.jar。 java 编译器在编译时更愿意在前置引导类路径中使用 类,从而覆盖 SDK 中的 android.jar。
见http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html
缺点是 IntelliJ/Android Studio 似乎无法在更高级别理解这一点,因此方法在编辑器中显示为红色,但构建工作正常。实际上,您可以将其与 provided
解决方案结合使用,以至少获得额外的 类 在编辑器中显示为有效,但暴露的 @hide
方法对于 IDE 仍然未知。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'], 'exclude': ['<PATH_TO_CUSTOM_ANDROID_API>.jar'])
provided files('libs/<PATH_TO_CUSTOM_ANDROID_API>.jar')
}
我使用它的时间还不够长,无法找到如何在 compilerArgs 中引用相对路径,但这应该很简单。
有一段时间我使用了 Xbootclasspath
技巧,但 Android Studio 不太喜欢它(突出显示已损坏),如果您尝试访问隐藏的,它根本不起作用API 在 SDK 级别上发生了变化(这些 API 是隐藏的,因此它们不稳定)。
最终我找到了一个解决方案,涉及创建在我的 Android 项目中使用的纯 java 库。我称此技巧为 "platform-unhidden" 又名 P-U。主要的烦恼是有更多的样板代码和库结构复杂。
如果您需要支持两个级别(比方说 API 级别 25 及更低,以及 API 级别 26 及更高),那么您将需要 5 个 java 库。 "stubs" 库包含从 AOSP 复制的 Android 平台代码,但像 SDK 中的 android.jar 一样,所有内容都被删除了,但带有您想要公开的隐藏方法。
"wrap" 库使用存根库并公开具有唯一名称的 methods/classes。 P-U 库根据检测到的 SDK 级别选择合适的包装库 Build.SDK_INT
.
视觉上看起来像这样:
+---------+ +----------+
+--api-->+ wrap-25 +--compile-only-->+ stubs-25 +--+
+-------+ | +---------+ +----------+ | +---------+
| +---+ +-->+ |
| P-U | | android |
| +---+ +-->+ |
+-------+ | +---------+ +----------+ | +---------+
+--api-->+ wrap-26 +--compile-only-->+ stubs-26 +--+
+---------+ +----------+
设置 java 库需要创建共享 gradle 文件,如下所示:
平台-shared.gradle:
// All the platform-* projects apply this gradle file
apply plugin: 'java'
apply plugin: 'java-library'
sourceCompatibility = 1.8
apply from: file("${androidBuild}/versions.gradle")
// ANDROID_HOME can override ANDROID_SDK_ROOT
// see https://developer.android.com/studio/command-line/variables
def androidHome = System.getenv('ANDROID_HOME')
def androidSdkRoot = System.getenv('ANDROID_SDK_ROOT')
if (androidHome?.trim() && new File(androidHome).canRead()) {
androidSdkRoot = androidHome
}
if (!androidSdkRoot?.trim() || !new File(androidSdkRoot).canRead()) {
def props = new Properties()
def file = new File("${rootDir}/local.properties")
if (file.exists()) {
file.withInputStream { props.load(it) }
if (props.getProperty("sdk.dir")) {
androidSdkRoot = props.getProperty("sdk.dir")
}
}
else {
throw new GradleException('Android SDK root not usable')
}
}
repositories {
// Need an Android SDK jar to compile against even though this is a pure java project
repositories {
flatDir {
dirs "${androidSdkRoot}/platforms/android-${COMPILE_SDK_VERSION}"
}
}
mavenCentral()
}
// Make CI happy by implementing these tasks
task assembleRelease {
dependsOn 'jar'
}
task assembleDebug {
dependsOn 'jar'
}
task assembleAndroidTest {
// Ignored
}
task lintRelease {
// Ignored
}
task lintDebug {
// Ignored
}
task testReleaseUnitTest {
// Ignored
}
task testDebugUnitTest {
// Ignored
}
然后你需要 gradle 每个库的文件,就像这样(没有显示 26,它看起来和 25 一样)。根据需要添加更多级别。
存根-25.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
api name: "android"
}
wrap-25.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
compileOnly project(':platform-stubs-25')
}
平台-unhidden.gradle:
apply from: file("../platform-shared.gradle")
dependencies {
compileOnly project(':platform-stubs-25')
compileOnly name: "android"
api project(':platform-wrap-25')
api project(':platform-wrap-26')
}