web-dev-qa-db-ja.com

Android NDK / JNI:他の共有ライブラリに依存する共有ライブラリの構築

Androidアプリは、NDKを使用して組み込まれた共有ライブラリにJNI呼び出しを行いたいと思っています。この共有ライブラリは、他の共有ライブラリによって提供される関数を呼び出します。他の共有ライブラリは他の場所でコンパイルされたCライブラリ。

これが私が試したものです:

私の環境:私はEclipseで作業しています。ネイティブサポートを追加し、jniライブラリを用意しました。そのライブラリには、コードと、他の.soファイルをコピーした\ libディレクトリがあります。

試行#1 Android.mk:ライブラリの場所を伝えるだけです

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE           := native_lib
LOCAL_SRC_FILES        := native_lib.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2

include $(BUILD_SHARED_LIBRARY)

これは問題なくビルドされますが、実行しようとすると、libsupport_lib1をロードできなかったためにdlopen(libnative_lib)が失敗したことを示すエラーが表示されます。

ここに来て私はこれを見つけました:

共有ライブラリは別の共有ライブラリを呼び出すことができますか?

必要なすべてのライブラリでロードライブラリを呼び出す必要があると述べました。すごい!

試行#2最初に各ライブラリを開く

static {
    System.loadLibrary("support_lib1");
    System.loadLibrary("support_lib2");
    System.loadLibrary("native_lib");
}

繰り返しますが、これは問題なくビルドされますが、実行すると新しいエラーが発生します。

libsupport_lib1をロードできませんでした。 findLibraryはnullを返しました。

今、私たちはどこかに着いています。ライブラリをターゲットにロードしてはなりません。

試行#3 .soファイルをproject/libs/armeabiにコピーしています

うまくいきませんでした。 Eclipseのビルド時に、そこにドロップしたファイルが削除されました。

試行#4ライブラリごとに新しいモジュールを作成する

それで私はこれを見つけました:

Android NDK:プリコンパイルされた静的ライブラリを使用したリンク

静的ライブラリについてですが、おそらく同じような問題が発生しています。要点は、ライブラリごとにモジュールを宣言する必要があるということです。新しいAndroid.mkは次のようになります。

LOCAL_PATH := $(call my-dir)

#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib1
LOCAL_SRC_FILES        := $(LOCAL_PATH)/lib/support_lib1.so
include $(BUILD_SHARED_LIBRARY)

#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib2
LOCAL_SRC_FILES        := $(LOCAL_PATH)/lib/support_lib2.so
include $(BUILD_SHARED_LIBRARY)

#build native lib
include $(CLEAR_VARS)    
LOCAL_MODULE           := native_lib
LOCAL_SRC_FILES        := native_lib.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2

include $(BUILD_SHARED_LIBRARY)

これはビルドします!さらに良いことに、armeabiはsosを持っています!でも[〜#〜] better [〜#〜]実行しようとすると、次のメッセージが表示されます(support_lib1と2がLoadLibraryによって開かれたことを通知します:

Lib /data/app-lib/com.example.tst/libsupport_lib1.soをロードしようとすると、共有ライブラリ/data/app-lib/com.example.tst/libsupport_lib1.so/data/app-lib /にJNI_OnLoadが見つかりませんでしたcom.example.tst/libsupport_lib1.so、initをスキップ

しかしその後... dlopenが失敗しました:libnative_lib.soによって参照されるシンボルfunc_that_exists_in_libsupport_lib.soが見つかりませんでした

編集:試行5:PREBUILT_SHARED_LIBRARYを使用します

だから私はこれを見つけました: ビルド済みの共有ライブラリをAndroid NDKプロジェクトにリンクするにはどうすればよいですか?

それはまさに私が求めているもののようです。彼らの答えは「build_shared_libraryを使用しないでください」ではなく、「PREBULILT_SHARED_LIBRARYを使用してください

よし、やってみよう。

 LOCAL_PATH := $(call my-dir)

#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib1
LOCAL_SRC_FILES        := $(LOCAL_PATH)/lib/support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)

#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib2
LOCAL_SRC_FILES        := $(LOCAL_PATH)/lib/support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)

#build native lib
include $(CLEAR_VARS)    
LOCAL_MODULE           := native_lib
LOCAL_SRC_FILES        := native_lib.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2

include $(BUILD_SHARED_LIBRARY)

ビルド...失敗!ビルドは欠落したシンボルについて不平を言います。

編集:試行6:すべてを平坦化

そこで、NDKの事前に作成されたドキュメントに戻りました。それは言う:

各ビルド済みライブラリは、ビルドシステムに対して単一の独立したモジュールとして宣言する必要があります。以下の簡単な例では、ファイル「libfoo.so」が以下のAndroid.mkと同じディレクトリにあると想定しています。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)

このようなモジュールを宣言するには、実際に必要なものは次のとおりです。

モジュールに名前を付けます(ここでは 'foo-prebuilt')。これは、ビルド済みライブラリ自体の名前に対応する必要はありません。

提供するビルド済みライブラリへのパスをLOCAL_SRC_FILESに割り当てます。いつものように、パスはLOCAL_PATHからの相対パスです。

共有ライブラリを提供する場合は、BUILD_SHARED_LIBRARYではなくPREBUILT_SHARED_LIBRARYを含めます。静的なものについては、PREBUILT_STATIC_LIBRARYを使用します。ビルド済みモジュールは何も構築しません。ただし、ビルド済みの共有ライブラリのコピーは$ PROJECT/obj/localにコピーされ、別のコピーは$ PROJECT/libs /にコピーされて削除されます。

ささいな例に合わせてすべてをフラットにしてみましょう。ライブラリを居心地の良い/ libフォルダーからコピーし、jniルートに配置しました。次にこれを行いました:

 LOCAL_PATH := $(call my-dir)

#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib1
LOCAL_SRC_FILES        := support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)

#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib2
LOCAL_SRC_FILES        := support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)

#build native lib
include $(CLEAR_VARS)    
LOCAL_MODULE           := native_lib
LOCAL_SRC_FILES        := native_lib.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2

include $(BUILD_SHARED_LIBRARY)

そして...同じエラー。さらに、ライブラリファイルが$ PROJECT/obj/localにコピーされているのを確認できません。

sooooo ....今何?

18
djc6535

あなたの問題は命名規則にあります。 NDKとAndroidは常にlibで始まる共有ライブラリ名を要求します。そうしないと、ライブラリは正しくリンクされず、libs/armeabiフォルダに正しくコピーされません。デバイスにインストールされていません(/data/data/package/libディレクトリに正しくコピーされています)。

support_lib1.soの名前をlibsupport_1.soに、support_lib2.solibsupport_2.soに、これらの2つのファイルをjni/libディレクトリに配置すると、Attempt#5は小さな変更で機能します。

LOCAL_PATH := $(call my-dir)

#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib1
LOCAL_SRC_FILES        := lib/libsupport_1.so
include $(PREBUILT_SHARED_LIBRARY)

#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE           := support_lib2
LOCAL_SRC_FILES        := lib/libsupport_2.so
include $(PREBUILT_SHARED_LIBRARY)

#build native lib
include $(CLEAR_VARS)    
LOCAL_MODULE           := native_lib
LOCAL_SRC_FILES        := native_lib.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2

include $(BUILD_SHARED_LIBRARY)

ところで、私はあなたがこれを必要とするとは思いません-L$(SYSROOT)/../usr/lib

[〜#〜] ps [〜#〜] Java側も更新することを忘れないでください:

static {
    System.loadLibrary("support_lib1");
    System.loadLibrary("support_lib2");
    System.loadLibrary("native_lib");
}
13
Alex Cohn

これがまさにあなたがいる場所なのかどうかはわかりませんが、これが私がこのようなことについて知っていることです。

  1. ビルド済みの各ライブラリを独自の個別のMakefileにします。 Android.mkの複数のターゲットは不安定になる傾向があります。悲しい。
  2. $(call import-add-path)および$(call import-module)を使用して各makeファイルを含めます
  3. _LOCAL_EXPORT__ファミリーの変数を使用して、ビルド済みのmakeファイルから可能な限りエクスポートします。

ビルド済み共有ライブラリAndroid.mk

_LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := my_module_name

MY_LIBRARY_NAME := shared_library_name

### export include path
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include

### path to library
LOCAL_SRC_FILES := libs/$(TARGET_Arch_ABI)/lib$(MY_LIBRARY_NAME).so

### export dependency on the library
LOCAL_EXPORT_LDLIBS := -L$(LOCAL_PATH)/libs/$(TARGET_Arch_ABI)/
LOCAL_EXPORT_LDLIBS += -l$(MY_LIBRARY_NAME)

include $(PREBUILT_SHARED_LIBRARY)
_

これは、ビルド済みのライブラリがこのようなdir構造にあると想定しています

_+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_Arch_ABI)/
  |- libshared_library_name.so
_

あなたが複数のABIのために構築していないのであれば、私はあなたがそれを少し残すことができると思います

プロジェクトのAndroid.mk

_LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := my_jni_module

## source files here, etc...

### define dependency on the other library
LOCAL_SHARED_LIBRARIES := my_module_name

include $(BUILD_SHARED_LIBRARY)

$(call import-add-path,$(LOCAL_PATH)/path/to/myLibraries/)
$(call import-module,SharedProjectFolderName)
$(call import-module,AnotherSharedProject)
_

すべての共有ライブラリを1つのフォルダに入れることをお勧めします。 $(call import-module,SharedProjectFolderName)と言うと、指定した検索パスに沿って_Android.mk_を含むフォルダーを検索します(_import-add-path_)

ところで、おそらくLOCAL_LDLIBS := -L$(SYSROOT)/../usr/libを指定すべきではありません。それはそれ自体でNDKから適切なライブラリを見つける必要があります。リンカーパスを追加すると、混乱する可能性があります。適切な方法は、サブモジュールからリンカーパスをフラグとしてエクスポートすることです。

また、_ndk-build V=1_を使用して、パスが見つからない理由などの大量の情報を取得できます。

4
yano

-Lオプションは、ライブラリを検索するディレクトリパスをリンカーに提供します。 -lオプションは、リンクするライブラリファイル名をリンカに提供します。ライブラリファイル名は「lib」で始まる必要があります。ライブラリの名前はlibsupport_lib1.soおよびlibsupport_lib2.soにする必要があります。あなたがそれをするなら、これはおそらくあなたがすべきことです(試行#1を置き換える):

LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib

リンカーは、-lを使用して指定したライブラリ名の前に「lib」を付け、その後ろに「.so」を付けます。 (なぜ-L $(SYSROOT)/../ usr/libがあるのですか?)

ライブラリを実行可能ファイルにリンクしなかったため、1番目と2番目の試行は失敗したと思います。これらは-lオプションでは言及されていません。ちなみに、これは自分で確認できます。 .apkファイルを解凍し、libディレクトリとサブディレクトリを確認します。あなたの.soファイルはそこにありますか?

エラーを見て:

but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so

メッセージ全体を提供できますか? dlopen()は、実行中のプロセスにライブラリをロードしてリンクします。

0
Sam