Android原生库链接到来自aar的另一个原生库

Android native library linking against another native library from aar

本文关键字:原生 aar 另一个 链接 Android      更新时间:2023-10-16

我有一个奇怪的问题。我有一个aar库,它包含并使用原生。so库。现在,我想写另一个库,它依赖于那个库,也有原生部分依赖于第一个库的原生库。依赖库同时使用第一个库中的本机代码和java包装器。

我想知道,是否有任何方法,如何通过标准gradle依赖(从第一个库复制头文件)做到这一点?或者我必须直接从源代码构建第二个库?

为什么我需要这个:我们有一个具有基本功能的多平台库,适用于android。这个库可以在标准的android应用程序中使用,我们在多个项目中使用它,这些项目没有其他本地代码。

但是在一个应用程序中,我们想编写多平台共享的应用程序代码,依赖于那个库,我想把这些库分开。

谢谢!

注意:此答案已弃用,因为Android Studio不再提供explored-aar目录。

对于一个更好的解决方案,从aar使用头,检查库androidNativeBundle

下面是OpenCV的一个可行的例子,你可以为first lib做同样的事情。

按如下方式准备第一个lib

jar, *.so和导出的headers打包(参见链接项目中的文件OpenCV4Android/opencv/build.gradle如何将头文件附加到aar)。

例如,你从first lib的建筑中得到first.aar

在其他项目中使用first lib

在你需要的时候添加first.aar到你的其他项目。

allprojects {
    repositories {
        jcenter()
        flatDir {
            dirs '/path/to/aar'
        }
    }
}
// in your app's build.gradle
dependencies {
    // ...
    compile 'com.example:example-with-header@aar'
    // ...
}

将本机库从本机构建系统链接到first.aar

如果你使用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} )

正如您提到的Gradle,我假设您正在使用最新的Android Studio。我使用1.2.2,发现它很容易从浮动的许多教程中构建简单的NDK项目但令人沮丧的是,很难构建一个NDK项目任何复杂性。我将总结我的发现,但我高度建议阅读这个博客和这个StackOverflow。

我的项目与你的相似,但不完全相同。对我来说,诀窍是就是想办法让Android Studio使用我的Android。可,然后找到正确的makefile变量。希望这对你有帮助。

Android Studio将完全忽略任何自定义Android。Mk文件,而不是自动生成自己的。要纠正这个错误,你必须首先破解build.gradle脚本,位于project/app/build.gradle。你也许能黑进顶级架构。Gradle,如果需要的话

这是我的hack build.gradle。我用的是Windows操作系统,所以我只针对Windows操作系统如果您使用的是OSX,则取消注释或Linux .

项目/应用程序/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 <<

现在我可以创建我的多库Android.mk。这个构建两个静态链接库,然后构建最后的动态链接前两个中的库和链接。所涉及的目录是:

目录:

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

Android.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)

你需要调整Android.mk.

从Android Studio 4.0开始,AGP (Android Gradle Plugin)支持一种名为Prefab的新机制。这使用了一个特别准备的AAR,可以在你的项目依赖关系中列出,就像你有其他外部库一样,你得到了从CMake或ndk-build链接到库所需的头和构建标志。

这类似于@alijandro的爆炸-aar方法,但它是官方支持的,并且需要更少的样板文件。例如,你只需要

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