在 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。

下面的方法可能更简单:

  1. How do I build the Android SDK with hidden and internal APIs available?得到framework_all.jar

  2. 将framework_all.jar放在项目中除了应用程序之外的system_libraries。

  3. 改变app/build.gradle喜欢

    依赖关系{ 编译文件树(目录:'libs',包括:['<em>.jar']) 提供的文件树(目录:'../system_libraries',包括:['</em>.jar']) }

  4. AS 会给出类似 "Gradle files have changed since .... Sync Now" 的提示,点击立即同步。

  5. 转到项目视图,您将在外部库中看到 platform_all.jar。

  6. 每次您更改 system_libraries 下的任何 .jar 时,您需要在 app/build.gradle 中做一些更改并重新同步它。

  1. 在../app下创建文件夹,命名为'systemlibs'
  2. 将您的图书馆复制到 'systemlibs'
  3. 在 app/build.gradle 依赖项中添加提供的文件 ('systemlibs/android.jar')
  4. 同步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')
}