【发布时间】:2014-01-28 23:51:17
【问题描述】:
可以通过JNI and NDK 从 Delphi 调用 Android C 函数。要实现这一点需要做很多工作,建议直接调用 NDK 函数。为此,我创建了一个小示例文件来按照我在 Delphi 源代码中找到的行来声明一个外部 C 函数。在<path to delphi>\source\rtl\android 中更具体。
我创建了一个非常小的测试程序来测试直接从 Delphi 调用 C 函数的功能。您将在下面找到所有源代码,这是我目前正在测试的。
unit DLL_external;
interface
const
MIDI_Lib = '/usr/lib/libmiditest.so';
test_fun = 'test_1';
function test_1 (n: Integer): Integer; cdecl;
external MIDI_Lib name test_fun;
implementation
initialization
finalization
end.
初始化和终结是必要的,因为否则会出现链接错误,因为缺少一些初始化和终结代码。调用类:
unit DLL_Test_Main;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
DLL_external;
//{$I Androidapi.inc}
type
TForm1 = class(TForm)
Button_Load: TButton;
Label1: TLabel;
procedure Button_LoadClick (Sender: TObject);
procedure FormCreate(Sender: TObject);
public
procedure call_external_function (value: Integer);
end; // Class: TForm1 //
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate (Sender: TObject);
begin
Label1.Text := 'External function not called yet';
end; // FormCreate //
procedure TForm1.Button_LoadClick (Sender: TObject);
begin
call_external_function (3);
end; // Button_LoadClick //
procedure TForm1.call_external_function (value: integer);
var n: Int32;
begin
n := test_1 (value);
Label1.Text := Format ('%d = test_1 (%d)', [n, value]);
end; // call_external_function //
end.
与本机库 miditest 一起使用。这是使用ndk-build 构建的。生成的库libmiditest.so 被复制到C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\android-ndk-r8e\platforms\android-14\arch-arm\usr\lib,因为这是 Delphi 放置自己的库的地方。
#include <jni.h>
int test_1 (int n) // little test for callability
{
return n * n;
}
当我执行ndk-build 时,会在子目录libs\armeabi-v7a 中生成一个文件libmiditest.so。我将此文件复制到<path to your ndk directory>\platforms\android-14\arch-arm\usr\lib。由于我一开始有一些链接错误(错误的名称和那种愚蠢的错误),我使用readelf -AWs libmiditest.so 来生成符号列表和库的预期架构。 test_1 这个名字和 arm v7 架构一样在符号列表中(我使用 Nexus 7 进行测试)。当我运行 Delphi 程序时,它立即在 Android 上崩溃:“不幸的是,DLL_Test_Propject 已停止”。检查 adb 输出(见下文),文件 libDLL_Test_Project.so 似乎是预期的。我用libDLL_Test_Project.so 替换了单元DLL_external 中的libmiditest.so 并将/usr/lib/libmiditest.so 复制到/usr/lib/libDLL_Test_Project.so。那没有帮助。
有谁明白为什么 Delphi 生成的应用程序会尝试加载自己的库?更好的是:有什么建议我应该如何通过 Delphi 调用 Android C 函数?
I/InputReader( 608): Reconfiguring input devices. changes=0x00000010
D/dalvikvm( 799): GC_FOR_ALLOC freed 2003K, 15% free 14582K/16964K, paused 29ms, total 29ms
I/PCKeyboard( 799): Loaded dictionary, len=841005
I/HK/LatinKeyboardBaseView( 799): onMeasure width=1200
I/HK/LatinKeyboardBaseView( 799): onMeasure width=1200
D/Documents( 3358): Used cached roots for com.android.providers.downloads.documents
D/Documents( 3358): Used cached roots for com.android.externalstorage.documents
D/Documents( 3358): Used cached roots for com.android.providers.media.documents
D/Documents( 3358): Used cached roots for com.google.android.apps.docs.storage
D/Documents( 3358): Update found 7 roots in 28ms
D/BackupManagerService( 608): Received broadcast Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.embarcadero.DLL_Test_Project flg=0x4000010 (has extras) }
V/BackupManagerService( 608): addPackageParticipantsLocked: #1
D/SystemBroadcastService( 987): Received broadcast action=android.intent.action.PACKAGE_ADDED and uri=
W/ContextImpl( 987): Implicit intents with startService are not safe: Intent { act=com.google.android.gms.games.service.INTENT } android.content.ContextWrapper.startService:494 com.google.android.gms.games.service.GamesIntentService.a:101 com.google.android.gms.games.service.GamesIntentService.b:368
I/ActivityManager( 608): Delay finish: com.android.vending/com.google.android.finsky.receivers.PackageMonitorReceiver$RegisteredReceiver
I/ActivityManager( 608): Resuming delayed broadcast
I/ActivityManager( 608): Delay finish: com.google.android.apps.plus/.service.PackagesMediaMonitor
I/ActivityManager( 608): Resuming delayed broadcast
V/GelStubAppWatcher( 3631): onReceive: android.intent.action.PACKAGE_ADDED
I/Icing.InternalIcingCorporaProvider( 3631): Updating corpora: A: com.embarcadero.DLL_Test_Project, C: MAYBE
I/ActivityManager( 608): START u0 {flg=0x10800000 cmp=com.estrongs.android.pop/.app.InstallMonitorActivity (has extras)} from pid 3466
D/dalvikvm( 608): GC_EXPLICIT freed 1385K, 11% free 19671K/22028K, paused 3ms+8ms, total 194ms
D/dalvikvm( 608): WAIT_FOR_CONCURRENT_GC blocked 24ms
D/AndroidRuntime( 4437): Shutting down VM
D/dalvikvm( 4437): GC_CONCURRENT freed 95K, 16% free 560K/660K, paused 0ms+0ms, total 2ms
W/InputMethodManagerService( 608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42c98f48 attribute=null, token = android.os.BinderProxy@42b91f10
D/dalvikvm( 3631): GC_CONCURRENT freed 560K, 6% free 10268K/10860K, paused 2ms+2ms, total 23ms
D/AndroidRuntime( 4476):
D/AndroidRuntime( 4476): >>>>>> AndroidRuntime START com.android.internal.os.RuntimeInit <<<<<<
D/AndroidRuntime( 4476): CheckJNI is OFF
D/dalvikvm( 4476): Trying to load lib libjavacore.so 0x0
D/dalvikvm( 4476): Added shared lib libjavacore.so 0x0
D/dalvikvm( 4476): Trying to load lib libnativehelper.so 0x0
D/dalvikvm( 4476): Added shared lib libnativehelper.so 0x0
D/dalvikvm( 4476): No JNI_OnLoad found in libnativehelper.so 0x0, skipping init
D/dalvikvm( 4476): Note: class Landroid/app/ActivityManagerNative; has 179 unimplemented (abstract) methods
D/AndroidRuntime( 4476): Calling main entry com.android.commands.am.Am
I/ActivityManager( 608): START u0 {flg=0x10000000 cmp=com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity (has extras)} from pid 4476
D/dalvikvm( 608): GC_FOR_ALLOC freed 807K, 12% free 19517K/22028K, paused 63ms, total 63ms
D/AndroidRuntime( 4476): Shutting down VM
D/dalvikvm( 4476): GC_CONCURRENT freed 96K, 15% free 586K/684K, paused 0ms+0ms, total 2ms
D/dalvikvm( 4507): Late-enabling CheckJNI
I/ActivityManager( 608): Start proc com.embarcadero.DLL_Test_Project for activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity: pid=4507 uid=10113 gids={50113, 3003, 1028, 1015}
I/dalvikvm( 4507): Enabling JNI app bug workarounds for target SDK version 9...
V/PhoneStatusBar( 667): setLightsOn(true)
D/AndroidRuntime( 4507): Shutting down VM
W/dalvikvm( 4507): threadid=1: thread exiting with uncaught exception (group=0x41ccbba8)
E/AndroidRuntime( 4507): FATAL EXCEPTION: main
E/AndroidRuntime( 4507): Process: com.embarcadero.DLL_Test_Project, PID: 4507
E/AndroidRuntime( 4507): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativ
eActivity}: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so
E/AndroidRuntime( 4507): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
E/AndroidRuntime( 4507): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
E/AndroidRuntime( 4507): at android.app.ActivityThread.access$800(ActivityThread.java:135)
E/AndroidRuntime( 4507): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
E/AndroidRuntime( 4507): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 4507): at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime( 4507): at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime( 4507): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 4507): at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime( 4507): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
E/AndroidRuntime( 4507): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
E/AndroidRuntime( 4507): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 4507): Caused by: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so
E/AndroidRuntime( 4507): at android.app.NativeActivity.onCreate(NativeActivity.java:183)
E/AndroidRuntime( 4507): at com.embarcadero.firemonkey.FMXNativeActivity.onCreate(FMXNativeActivity.java:67)
E/AndroidRuntime( 4507): at android.app.Activity.performCreate(Activity.java:5231)
E/AndroidRuntime( 4507): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
E/AndroidRuntime( 4507): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
E/AndroidRuntime( 4507): ... 11 more
W/ActivityManager( 608): Force finishing activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity
I/WindowManager( 608): Screenshot max retries 4 of Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNat
iveActivity t17 f}} appWin=Window{42a1bab8 u0 Starting com.embarcadero.DLL_Test_Project} drawState=4
W/WindowManager( 608): Screenshot failure taking screenshot for (1200x1920) to layer 21015
W/ActivityManager( 608): Activity pause timeout for ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17 f}
E/WindowManager( 608): Starting window AppWindowToken{4308ff58 token=Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17}}} timed out
I/Process ( 4507): Sending signal. PID: 4507 SIG: 9
W/InputMethodManagerService( 608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42a1abd0 attribute=null, token = android.os.BinderProxy@42b91f10
I/ActivityManager( 608): Process com.embarcadero.DLL_Test_Project (pid 4507) has died.
D/Finsky ( 1567): [1] 5.onFinished: Installation state replication succeeded.
更新 1
从我收集到的 cmets 中,代码可能是更大系统的一部分。这段代码是一个小的独立程序。原生代码库确实和你在这里看到的一样小。
更新 2
正如 Arioch'The 指出的那样,通过使用静态链接(或 Windows 术语中的隐式加载),当库未加载时,主程序将不会加载。这就解释了上面提到的adb 消息。因此,问题是:为什么 libmiditest.so 不加载?
【问题讨论】:
-
it appears that the file libDLL_Test_Project.so is expected当然。那就是 Delphi/Android 包含所有编译代码的文件,它是必需的。I replaced the libmiditest.so in unit DLL_external毫无意义。您了解 DLL/SO 的无条件静态绑定与使用 Win32 LoadLibrary 等动态条件加载之间的区别吗?最有可能发生的是 Android 无法加载链接的 unc。英石。绑定了libmidi,因此无法加载libmiditest。所以我认为静态绑定只允许文件名而不是路径。 -
1) '有谁知道为什么 Delphi 生成的应用程序会尝试加载自己的库?' - 作为基于 NDK 的应用程序,Delphi 代码被编译为 .so,然后由 Java 存根可执行文件参与。 2)您可以尝试先链接到 libsonivox.so 吗?不做任何实质性的事情,只是为了看到链接到系统提供的库正常工作。
-
app-library 通过静态绑定使 libmiditest 成为必需的依赖项,即前提条件。如果后者无法加载,那么前者也无法加载。stackoverflow.com/questions/17726488
-
我想尝试学习如何在你的库已经加载并运行之后在 linux 中动态加载它,就像我上面所说的那样。 en.wikipedia.org/wiki/…
-
@Arioch - 成功了!感谢您的建议。看答案。现在开始在 Delphi 中进行 MIDI 编程:-D
标签: android c linux delphi android-ndk