【问题标题】:How to prepare a closed sourced SDK module on Android Studio?如何在 Android Studio 上准备闭源 SDK 模块?
【发布时间】:2015-07-22 12:41:53
【问题描述】:

背景

我正在开发一个非常受欢迎的应用程序,以至于它的一部分应该成为 SDK(可供开发人员使用),并且该应用程序将拆分为 2 个应用程序(两者都使用SDK)。

据我所知,有多种方法可以创建 SDK 模块(以前在 Eclipse 上称为“项目”):

  1. 完全开源(Android 库) - 所有源和资源都是开源的,可以修改。一个例子可能是 Facebook 的 SDK 和大量的 Github 存储库。

  2. 单个 Jar 文件,可以关闭源代码。

问题

遗憾的是,我无法将 SDK 开源,并且它应该相对受到保护而不是窥探(混淆等...)。

这里的问题是,SDK 需要使用自己的一些资源(drawables、strings、...),到目前为止(因为我没有很多创建 SDK 的经验)我发现处理 SDK 资源的 2 种方法:

  1. 使用反射和/或“context.getResources().getIdentifier”。这很混乱,因为我失去了代码的整个“R”用法。此外,正如我在here 中所写的那样,它存在“styleable”问题。这也使得找到未使用的资源变得困难。

  2. 更糟糕的方法:将资源放入 assets 文件夹,将文件以古怪的方式放入 jar 文件中,...

请注意,SDK 的一部分包含自定义视图(例如,从 TextView 扩展的类),因此即使我将 SDK 拆分为 2 个模块 - 资源和 java 文件,两者都可能存在依赖问题(每个都使用另一个)。

问题

有没有办法解决这个问题?

SDK 的代码部分是否可以保持封闭源代码,像往常一样访问“R”文件,让我和任何使用 SDK 的人都能轻松使用?

然后我将如何生成通过 Android Studio 混淆的 jar 文件?是否可以准备好以后通过 gradle 使用它?

我可以将 SDK 的 Android 库制作成一个模糊的 jar 文件,而不用担心“R”文件吗?我问这个是因为这样我可以享受两个世界:对于我们的应用程序,它将保持开源,而对于第三方应用程序,它将是封闭源代码。


编辑:看到这应该很容易,我自己尝试过。我创建了一个全新的 POC 项目,其中包含一个名为“sdkmodule”的 Android 库模块,并将此类添加到其中:

public class SdkClass
      {
      public String doIt(Context context)
        {
        return context.getResources().getString(R.string.app_name);
        }
      }

然后,我让应用程序的模块使用这个,我在里面写了这段代码:

  @Override
  protected void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    SdkClass sdkClass=new SdkClass();
    Log.d("AppLog","string from SDK:"+sdkClass.doIt(this));
    Log.d("AppLog","string with same ID name from app:"+getResources().getString(R.string.app_name));
    }

我的预期是第一个日志会打印 SDK 模块中的字符串,第二个日志会显示当前项目的字符串,但我得到了一个异常:

java.lang.NoClassDefFoundError: Failed resolution of: Lcom/example/user/sdkmodule/R$string;

在另一次尝试中,我得到了与应用程序本身(使用 SDK 模块的那个)相同的字符串。而且,在另一次运行中,SDK 生成了我想要的所需字符串。

怎么可能?我该怎么办?

此外,我尝试在 SDK 本身中创建第二个活动,并在那里创建了一个资源,该资源与应用程序本身具有相同的资源名称(用于其布局中的 textView),但是具有不同的值,但是当我到达此活动时,我已经看到了应用程序使用的那个。

这是项目,已压缩(忽略文件夹的名称,我也想尝试风味):

https://drive.google.com/file/d/0B-PZZGk2vPohX25WUDNKTmotUTg/view?usp=sharing

【问题讨论】:

  • Android 库项目的 AAR 格式已可用于 Android Studio 和 Gradle for Android 一年多。 Maven 用户也可以直接使用 AAR。对于 Eclipse/Ant,将 AAR 解压缩到库项目中(使用已编译的 JAR 而不是源代码)并不难,至少对于基本的东西来说(here's a blog post 解释了该过程)。 Play Services SDK 以这种基本方式提供,使用编译后的 Java 字节码而不是源代码。
  • 假设我有一个正在运行的应用程序,其中一个模块应该是 SDK(或者如果您愿意,可以使用 2 个模块 - 一个用于资源,一个用于源代码),下一步应该是什么?您提供的网站是 Eclipse 的,它是用于使用 AAR 而不是准备它(尽管知道如何使用它也很重要)。
  • “您给出的网站是为 Eclipse 设计的,它是用于使用 AAR 而不是准备它”——这就是为什么它在讨论使用 AAR 的句子末尾的括号中日食。

标签: android android-studio sdk obfuscation r.java-file


【解决方案1】:

您的问题的答案是将您的库打包并分发为AAR bundle

此格式允许您提供经过混淆的 SDK jar 及其资源和 R 映射文件。

这个格式是一个标准并且被maven-android-plugin完全支持(实际上它是旧的APKLib格式的替换,它只支持源文件的分发)。

当然 Gradle 和 Android Studio 也支持它。

【讨论】:

  • 问题是,我还能像往常一样使用 R,有没有关于如何使用它的好教程?办公室的人已经制作了 AAR 并将其放在 gradle 上,但他们已将 R 文件设为未使用并在任何地方使用“getIdentifier”。我认为这是一件坏事,但是我找不到合适的教程,而且我没有 AAR 相关主题的经验,而且我上次制作 SDK 是在很久以前,使用 Eclipse。
  • 假设我有一个正在运行的应用程序,其中一个模块应该是 SDK(或 2 个模块,如果您愿意,一个用于资源,一个用于源代码),下一步应该是什么?
  • 您必须明确告诉使用 SDK 依赖库的应用。在编译时,两个 R 文件将被合并,一切都会完美运行。如果您想为 SDK 提供资源和经过混淆的源代码,则无需使用不同的模块。一个就够了。由于我目前正在发布一个项目,所以我没有时间创建教程,但让我检查一下我是否发现了一些有趣的东西。你使用 Gradle 或 Maven(或者不幸的是,它们都不使用)?
  • @androiddeveloper 你真的不需要在库中做任何特别的事情。它有一个 res 文件夹并自动生成一个 R 类,您可以像以前一样在库中使用它。当您编译最终 APK 时,编译器会负责合并资源。
  • @joshua-carmody 在回复中为 Android Studio 提供了一个很好的教程。如果您使用的是Maven(建议使用最新发布的版本),您只需将打包格式改为aar而不是apk,它会直接创建AAR模块。
【解决方案2】:

Android Archive (AAR) format 可以满足您的需求。它就像一个特定于 Android 的 JAR 文件,包含已编译的代码,但包含自己的资源和清单。您还可以将混淆作为构建过程的一部分。默认情况下,当前版本的 Android Studio (1.2) 和 Gradle 会自动为您在项目中创建的所有库模块构建 .AAR 文件。

您可以将应用模块更改为将发布 AAR 文件的库项目,只需在模块的 Gradle 文件中将 apply plugin: 'com.android.application' 更改为 apply plugin: 'com.android.library'。 .AAR 文件将在每次构建后放置在您的MODULENAME/build/outputs/aar 文件夹中。一些more information is available here

编辑 1,问题更新后:

当最终的 APK 编译完成时,您的 AAR 中的资源会与应用模块合并。应用程序资源将覆盖库的。这可能是设计使然,允许使用 3rd 方库的人在创建他们的应用程序时自定义其资源,而无需重新构建库。我认为解决资源冲突问题的最简单方法就是将 sdkmodule 资源命名为更独特的名称。为什么不将字符串键设为R.string.com_example_sdk_name 之类的?

不,AAR 库默认不会被混淆,但您可以set up ProGuard in the Gradle build file 让您的 AAR 库处理此问题。也可以使用其他工具。

【讨论】:

  • 默认情况下生成的“sdkmodule-release.aar”是否真的被混淆了?另外,为什么我不能使用(甚至在 SDK 模块本身中)在 SDK 模块中定义的资源,并且它们会被应用程序的模块本身覆盖?我已经更新了问题。
  • 如何到达 SDK 的代码成功获取了 SDK 的字符串资源,而当我使用布局文件和活动本身(上传后添加代码)时,它得到了覆盖的字符串资源?是否可以避免“垃圾邮件”使用 SDK 和 R.java 文件中的 SDK 资源的人?
  • 另外,关于proguard,我所做的只是添加这些行,SDK模块会被混淆吗?如果是这样,什么会触发它被混淆?会是“生成签名的APK”吗?或者是其他东西?或者,正如我所想(根据所写的),我可以制作一种新口味,仅此而已?
  • 我明白了,您能回答一下资源“垃圾邮件”问题吗?是否可以在不使用非常独特的资源名称的情况下避免覆盖资源?
  • @androiddeveloper - 我自己的实验表明,您库中的任何资源都将包含在使用它构建的任何应用程序和 APK 中,这是有道理的。我无法找到任何方法来避免覆盖没有唯一名称的资源,但我发现的文档很少。
猜你喜欢
  • 2014-02-08
  • 2015-09-09
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多