Dificultades para llamar directamente a una función NDK de Android directamente de Delphi

Es posible llamar a una función C de Android desde Delphi a través de JNI y NDK . Para implementar esto es bastante trabajo y se sugirió llamar directamente a las funciones NDK. Para ello creé un pequeño archivo de ejemplo para declarar una función C externa a lo largo de las líneas que encontré en el código fuente de Delphi. Más específico en <path to delphi>\source\rtl\android .

He creado un testprogram muy pequeño para probar la funcionalidad de llamar a una función C directamente de Delphi. Todo el código fuente que encontrará a continuación, esto es lo que estoy probando actualmente.

  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. 

La inicialización y la finalización son necesarias porque, si se producen errores de vinculación, se hace referencia a algún código de inicialización y finalización que falta. La clase llamante:

  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. 

Junto con una biblioteca nativa miditest . Esto se creó utilizando ndk-build . La biblioteca resultante libmiditest.so se copió en C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\android-ndk-r8e\platforms\android-14\arch-arm\usr\lib ya que este es el lugar donde Delphi ha colocado sus propias bibliotecas.

  #include <jni.h> int test_1 (int n) // little test for callability { return n * n; } 

Cuando hago un ndk-build libmiditest.so se produce en el subdirectorio libs\armeabi-v7a . Copié este archivo en <path to your ndk directory>\platforms\android-14\arch-arm\usr\lib . Como tuve algunos errores de enlace al principio (nombres erróneos y ese tipo de errores estúpidos) usé readelf -AWs libmiditest.so para producir una lista de símbolos y la arquitectura esperada de la biblioteca. El nombre test_1 estaba en la lista de símbolos como era la arquitectura de brazo v7 (uso un Nexus 7 para pruebas). Cuando ejecuto el programa Delphi se bloquea inmediatamente en Android: "Desafortunadamente, DLL_Test_Propject se ha detenido". Examinando la salida del anuncio (véase abajo) aparece que el archivo libDLL_Test_Project.so se espera. He reemplazado libmiditest.so en la unidad DLL_external por libDLL_Test_Project.so y libDLL_Test_Project.so copiado /usr/lib/libmiditest.so en libDLL_Test_Project.so . Eso no ayudó.

¿Alguien entiende por qué la aplicación generada por Delphi intenta cargar una biblioteca por sí misma? Y mejor: cualquier sugerencia de cómo debería llamar a una función C de Android a través de Delphi?

  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. 

Actualización 1

De los comentarios que recogí que el código podría ser parte de un sistema más grande. Este código es un pequeño programa autónomo. La biblioteca de código nativo es tan pequeña como se ve aquí.

Actualización 2

Como Arioch'The señala es que mediante el uso de enlaces estáticos (o carga implícita en términos de Windows) el programa principal no se cargará cuando la biblioteca no se carga. Eso explica el mensaje adb mencionado anteriormente. La querstion es así: ¿por qué libmiditest.so no carga?

Una solución es la vinculación dinámica de la biblioteca como lo sugiere Arioch'The en sus comentarios. El mecanismo de cómo hacerlo se describe en su enlace wiki. Para enlazar en la biblioteca use dlopen de la unidad Posix.Dlfcn .

Uno tiene que proporcionar un camino a la biblioteca sin embargo. En el código de ejemplo a continuación he creado el directorio Data\d en el directorio sdcard0 y libmiditest.so copiado libmiditest.so en él. Este directorio puede ser diferente en otros sistemas. De hecho, esto podría explicar la razón por la que el enlace estático no funciona (todavía). La biblioteca no se copia en las rutas que el sistema busca al buscar bibliotecas.

  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. 
  • Servidor local HTTPS en Android con autenticación de cliente
  • Android 2.3: Sistema de archivos de sólo lectura pegado
  • El dispositivo no responde a la dirección establecida
  • Comandos ADB de Android para obtener las propiedades del dispositivo
  • Plataforma de Córdoba añaden problema androide extraño
  • Ningún paquete lib32z1, lib32ncurses5, lib32stdc ++ 6 disponible en centos
  • EGLImages con renderbuffer como hermano de origen y textura como hermano objetivo
  • Android dumpsys batteryinfo vs android dumpsys batterystats
  • Android - Comando no encontrado
  • ¿Cómo encontrar implementaciones de llamada de sistema en Linux / Android 2.6.29?
  • Fastboot y adb no funcionan con sudo
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.