如何在 Android Studio 上准备闭源 SDK 模块?

How to prepare a closed sourced SDK module on Android Studio?

背景

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

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

  1. 完全开源(Android库)——所有来源和资源都是开源的,可以修改。一个例子可能是 Facebook 的 SDK 和很多 Github 回购。

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

问题

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

这里的问题是,SDK 需要使用它自己的一些资源(可绘制对象、字符串等),到目前为止(因为我没有太多创建 SDK 的经验)我'我们找到了 2 种处理 SDK 资源的方法:

  1. 使用反射 and/or "context.getResources().getIdentifier" 。这非常混乱,因为我丢失了代码的整个 "R" 用法。此外,它与 "styleable" 有问题,正如我写的 here。这也使得很难找到未使用的资源。

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

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

问题

是否有可能以某种方式解决这个问题?

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

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

我能否将 SDK 的 Android-library 制作成混淆的 jar 文件而不用担心 "R" 文件?我问这个是因为这样我可以享受两个世界:对于我们的应用程序,它将保持开源,而对于第三方应用程序,它将是闭源的。


编辑:看到这应该很容易,我自己试过了。我创建了一个全新的 POC 项目,它有一个名为 "sdkmodule" 的 Android 库模块,并向其中添加了这个 class:

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 本身中制作第二个 activity,并且我在那里创建了一个与资源名称相同的资源(用于其布局中的 textView)应用程序本身,但具有不同的值,但是当我达到此 activity 时,我看到了该应用程序使用的值。

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

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

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

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

此格式是标准格式,完全受 maven-android-plugin 支持(实际上它是对仅支持源文件分发的旧 APKLib 格式的替换)。

当然 Gradle 和 Android Studio 也支持它。

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

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

编辑 1,问题更新后:

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

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