草庐IT

android - 直接从 Delphi 调用 Android NDK 函数的困难

coder 2023-06-19 原文

可以通过 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 时一个文件 libmiditest.so在子目录libs\armeabi-v7a中产生.我把这个文件复制到<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_Project 已停止”。检查 adb 输出(见下文)似乎文件 libDLL_Test_Project.so是期待。我更换了 libmiditest.so在单元 DLL_external by libDLL_Test_Project.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

根据我收集到的评论,该代码可能是更大系统的一部分。此代码是一个小型独立程序。 native 代码库确实和您在此处看到的一样小。

更新 2

正如 Arioch 所指出的,通过使用静态链接(或 Windows 术语中的隐式加载),当库未加载时,主程序将不会加载。这解释了 adb上面提到的消息。因此问题是:为什么 libmiditest.so 不加载?

最佳答案

一种解决方案是按照 Arioch'The 在其评论中的建议动态绑定(bind)库。他的 wiki 链接中描述了如何做到这一点的机制。要在库中链接,请使用 Posix.Dlfcn 单元中的 dlopen

但是必须提供一个到库的路径。在下面的示例代码中,我在 sdcard0 目录中创建了目录 Data\d 并将 libmiditest.so 复制到其中。该目录在其他系统上可能不同。事实上,这或许可以解释静态绑定(bind)(目前)不起作用的原因。在查找库时,库不会复制到系统搜索的路径中。

  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,
     Posix.Dlfcn, FMX.Layouts, FMX.Memo, System.Math;


  const
     lib_path = '/storage/sdcard0/Data/d/libmiditest.so';
     fun_name = 'test_1';

  type
     TForm1 = class(TForm)
        Button_Load: TButton;
        Memo1: TMemo;

        procedure Button_LoadClick (Sender: TObject);
        procedure FormCreate(Sender: TObject);

      protected
         Lib_Handle: THandle;
      public
         procedure call_external_function (value: Integer);
      end; // Class: TForm1 //

  var
     Form1: TForm1;

  implementation

  {$R *.fmx}

  procedure TForm1.FormCreate (Sender: TObject);
  begin
     Lib_Handle := THandle (dlopen (lib_path, RTLD_LAZY));
     if Lib_Handle = 0 then
     begin
        Memo1.Lines.Add ('Cannot open library: ' + lib_path);
     end else
     begin
        Memo1.Lines.Add ('Opened library: ' + lib_path);
     end; // if
  end; // FormCreate //

  procedure TForm1.Button_LoadClick (Sender: TObject);
  begin
     call_external_function (RandomRange (0, 99));
  end; // Button_LoadClick //

  procedure TForm1.call_external_function (value: integer);
  var
     test_1: function (n: integer): integer; cdecl;
     n: Int32;
  begin
     if Lib_Handle <> 0 then
     begin
        test_1 := dlsym (Lib_Handle, fun_name);
        if not assigned (test_1) then
        begin
           Memo1.Lines.Add ('Cannot create function: ' + fun_name);
        end else
        begin
           n := test_1 (value);
           Memo1.Lines.Add (Format ('%d = %s (%d)', [n, fun_name, value]));
        end;
     end;
  end; // call_external_function //

  end.

关于android - 直接从 Delphi 调用 Android NDK 函数的困难,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21036994/

有关android - 直接从 Delphi 调用 Android NDK 函数的困难的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  3. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  4. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  7. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  8. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

随机推荐