简体   繁体   English

Android 本机库链接到来自 aar 的另一个本机库

[英]Android native library linking against another native library from aar

I have a curious question.我有一个好奇的问题。 I have an aar library, which contains and uses native .so library.我有一个 aar 库,它包含并使用本机 .so 库。 Now, I want to write another lib, which depends on that library and also has native part depending on native lib from the first lib.现在,我想编写另一个库,它依赖于该库,并且还具有依赖于第一个库中的本机库的本机部分。 The dependent library uses both the native code and java wrappers from the first lib.依赖库同时使用第一个库中的本机代码和 java 包装器。

I wonder, is there any way, how to do this by standard gradle dependency(with copied headers files from the first lib)?我想知道,有什么办法可以通过标准的 gradle 依赖(从第一个库复制头文件)来做到这一点? Or have I to build the second lib directly from the sources?还是我必须直接从源代码构建第二个库?

Why I need this: We have a multiplatform lib with basic functionality, for android as aar.为什么我需要这个:我们有一个具有基本功能的多平台库,用于 android 作为 aar。 This lib can be used in standard android app and we use it in multiple projects, which have no another native code.这个库可以在标准的 android 应用程序中使用,我们在多个项目中使用它,没有其他本机代码。

But in one app we want to write multiplatform shared app code, depending on that lib and I want to have these libs separated.但是在一个应用程序中,我们想要编写多平台共享应用程序代码,这取决于该库,我希望将这些库分开。

Thanks!谢谢!

NOTE : This answer is deprecated, since Android Studio on longer provide explored-aar directory.注意:此答案已弃用,因为 Android Studio 不再提供explored-aar目录。

For a better solution to use headers from aar , check the library androidNativeBundle要获得使用来自aar标头的更好解决方案,请查看库androidNativeBundle

Here is a workable example basic on OpenCV , you can do the same for your first lib .是一个基于OpenCV的可行示例,您可以对first lib执行相同的操作。

Prepare first lib as following准备第一个库如下

Package the jar , *.so , and exported headers (see the file OpenCV4Android/opencv/build.gradle in the linked project how to append the headers to aar).打包jar*.so和导出的headers (请参阅链接项目中的文件OpenCV4Android/opencv/build.gradle如何将头文件附加到 aar)。

You get first.aar for example from the building of first lib .例如,您从first lib的构建中获得first.aar

Using first lib in your other projects在其他项目中使用第一个库

Add the first.aar in your other projects when you needed.需要时在其他项目中添加first.aar

allprojects {
    repositories {
        jcenter()

        flatDir {
            dirs '/path/to/aar'
        }
    }
}

// in your app's build.gradle
dependencies {
    // ...
    compile 'com.example:example-with-header@aar'
    // ...
}

Link against your native library to the first.aar from your native build system.将您的本机库链接到您本机构建系统中的first.aar

If you use CMake , it should look like this如果你使用CMake ,它应该是这样的

add_library(first
SHARED
IMPORTED)

set_target_properties(
first
PROPERTIES IMPORTED_LOCATION
../../../../build/intermediates/exploded-aar/org.example/example-debug/jni/${ANDROID_ABI}/libfirst.so
# use find command to figure out the location of the first lib before use it, not sure if it's different in different build environment
# for android studio gradle plugin latest version use
# ../../../../build/intermediates/transforms/mergeJniLibs/debug/folders/2000/1f/main/lib/${ANDROID_ABI}/libfirst.so
)

# also use find command to figure the actual location of the exported header from aar
# this might be different in your environment for different version of gradle plugin
include_directories(build/intermediates/exploded-aar/com.example/example-debug/cpp/include)

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       first
                       ${log-lib} )

As you mentioned Gradle, I am assuming you are using the latest Android Studio.正如您提到的 Gradle,我假设您使用的是最新的 Android Studio。 I am using 1.2.2 and found it easy to build the simple NDK projects from the many tutorials floating around, but frustratingly difficult to build an NDK project of any complexity.我使用的是 1.2.2 并发现从许多浮动教程中构建简单的 NDK 项目很容易,但构建任何复杂的 NDK 项目却非常困难。 I will summarize what I found, but I highly suggest reading this blog and this StackOverflow .我将总结我的发现,但我强烈建议阅读此博客此 StackOverflow

My project is similar to yours but not exactly.我的项目与您的相似,但不完全相同。 The trick for me was figuring out how to make Android Studio use my Android.mk, then finding the right makefile variables.我的诀窍是弄清楚如何让 Android Studio 使用我的 Android.mk,然后找到正确的 makefile 变量。 Hopefully this will help.希望这会有所帮助。

The stock Android Studio will completely ignore any custom Android.mk file you create, and instead auto-generate its own.原生 Android Studio 将完全忽略您创建的任何自定义 Android.mk 文件,而是自动生成自己的文件。 To correct this, you must first hack the build.gradle script for your project, located at project/app/build.gradle .要更正此问题,您必须首先破解位于project/app/build.gradlebuild.gradle脚本。 You could probably hack the top-level build.gradle, if desired.如果需要,您可能会破解顶级 build.gradle。

This is my hacked build.gradle.这是我被黑的 build.gradle。 I build on a Windows box, so I hacked it for Windows only.我在 Windows 机器上构建,所以我只为 Windows 破解了它。 Uncomment the lines if you are using OSX or Linux.如果您使用的是 OSX 或 Linux,请取消注释这些行。

project/app/build.gradle:项目/应用程序/build.gradle:

//import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'

android {
  compileSdkVersion 22
  buildToolsVersion "22.0.1"

  defaultConfig {
    applicationId "com.sample.app"
    minSdkVersion 15
    targetSdkVersion 22
    versionCode 1
    versionName "1.0"
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  //ENABLE CUSTOM ANDROID.MK >>
  sourceSets.main.jni.srcDirs= [] //Disable automatic ndk-build.
  sourceSets.main.jniLibs.srcDir 'src/main/libs'

  //Call regular ndk-build script from app directory
  task ndkBuild(type: Exec) {
    workingDir file('src/main')
    commandLine getNdkBuildCmd()
  }

  tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
  }

  task cleanNative(type: Exec) {
    workingDir file('src/main')
    commandLine getNdkBuildCmd(), 'clean'
  }

  clean.dependsOn cleanNative
}
//ENABLE CUSTOM ANDROID.MK <<

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:22.2.0'
  compile 'com.google.android.gms:play-services:7.5.0'
}

//ENABLE CUSTOM ANDROID.MK >>
def getNdkDir() {
  if (System.env.ANDROID_NDK_ROOT != null)
    return System.env.ANDROID_NDK_ROOT

  Properties properties = new Properties()
  properties.load(project.rootProject.file('local.properties').newDataInputStream())
  def ndkdir = properties.getProperty('ndk.dir', null)
  if (ndkdir == null)
    throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file")

  return (ndkdir)
}

def getNdkBuildCmd() {
  def ndkbuild = getNdkDir() + "/ndk-build.cmd"
//  def ndkbuild = getNdkDir() + "/ndk-build"
//  if (Os.isFamily(Os.FAMILY_WINDOWS))
//    ndkbuild += ".cmd"
  return ndkbuild
}
//ENABLE CUSTOM ANDROID.MK <<

Now I can create my multi-library Android.mk.现在我可以创建我的多库 Android.mk。 This builds two static-link libraries, then builds the final dynamic-link libraries and links in the first two.这将构建两个静态链接库,然后构建最终的动态链接库和前两个中的链接。 The directories involved are:涉及的目录是:

Directories:目录:

project/
  app/
    build.gradle
    src/
      main/
        java/
        jni/
          Android.mk
          include/
          libmp3lame/
          MiniMp3/
          mp3_jni.c
          mpglib/

Android.mk:安卓.mk:

TOP_PATH := $(call my-dir)

LOCAL_PATH := $(TOP_PATH)/libmp3lame
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_CFLAGS := -DSTDC_HEADERS

LOCAL_MODULE                := libmp3lame
LOCAL_SRC_FILES             := \
bitstream.c \
...
version.c

include $(BUILD_STATIC_LIBRARY)
MY_LOCAL_STATIC_LIBRARIES += libmp3lame

LOCAL_PATH := $(TOP_PATH)/mpglib
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_C_INCLUDES += $(TOP_PATH)/libmp3lame
LOCAL_CFLAGS := -DSTDC_HEADERS
LOCAL_MODULE := mpglib
LOCAL_SRC_FILES := \
common.c \
...
tabinit.c

include $(BUILD_STATIC_LIBRARY)
MY_LOCAL_STATIC_LIBRARIES += mpglib

LOCAL_PATH := $(TOP_PATH)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_CFLAGS := -DSTDC_HEADERS
LOCAL_MODULE := Mp3
LOCAL_STATIC_LIBRARIES := $(MY_LOCAL_STATIC_LIBRARIES)
LOCAL_LDLIBS := -llog
LOCAL_SRC_FILES := \
./mp3_jni.c

include $(BUILD_SHARED_LIBRARY)

You will need to tweak the Android.mk.您将需要调整 Android.mk。

From Android Studio 4.0, a new mechanism called Prefab is supported by AGP (Android Gradle Plugin).从 Android Studio 4.0 开始,AGP(Android Gradle 插件)支持一种名为Prefab的新机制。 This uses a specially prepared AAR that can be listed in your project dependencies just like you have other external libraries, and you get the headers and build flags necessary to link to the library from CMake or ndk-build.这使用了一个专门准备的 AAR,它可以像您拥有其他外部库一样列在您的项目依赖项中,并且您可以获得从 CMake 或 ndk-build 链接到库所需的头文件和构建标志。

This resembles the exploded-aar approach by @alijandro, but it's officially supported, and requires much less boilerplate.这类似于@alijandroexploded-aar 方法,但它得到官方支持,并且需要更少的样板文件。 Eg you will only need例如你只需要

find_package(first REQUIRED CONFIG)
target_link_libraries(app first::libfirst)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM