domingo, 17 de marzo de 2013

Integración en Java de código en C++


El principal problema del uso de las librerías OpenCv para nuestro proyecto fue el hecho de que éstas vienen escritas para el lenguaje orientado a objetos c++, y no para Java.

Sin embargo, el entorno de desarrollo de Oracle permite la importación de bibliotecas escritas en c++ mediante el llamado JNI (Java Native Interface). Mediante éste método, y siguiendo los pasos desritos adelante, se pueden importar en cualquier archivo escrito en java bibliotecas escritas en c/c++, de igual manera que si éstas estuvieran escritas en java, pudiendo hacer uso de objetos, métodos, etc.

Para la consecución de lo comentado anteriormente, se necesitó, evidentemente, guardar en un directorio todas aquellos ficheros en c/c++ que queramos incluir en nuestro código Java. Fue necesario que se guardaran de la siguiente manera:

NombreDelFichero_jni.cpp

Después de ello, se procedió a su preparación para incluirlas en el código Java. Para ello una pieza fundamental fueron los ficheros .mk. Dichos ficheros deben estar incluidos en el directorio (que de ahora en adelante llamaremos jni) en el que se encontraban los ficheros escritos en código nativo. En dichos ficheros fue necesario incluir el siguiente código:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#OPENCV_CAMERA_MODULES:=off
#OPENCV_INSTALL_MODULES:=off
#OPENCV_LIB_TYPE:=SHARED
include ../../sdk/native/jni/OpenCV.mk

LOCAL_SRC_FILES  := <NombreDelFichero_jni.cpp>
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_LDLIBS     += -llog -ldl

LOCAL_MODULE     := <Nombre_Del_Fichero>

include $(BUILD_SHARED_LIBRARY)

En los que sustituyó lo encerrado por llaves por los ficheros que deseamos convertir a biblioteas listas para usar en Java.

Una vez hecho esto, fue necesario hacer el "build" de ellas para poder incluirlas en Java. Dicho proceso lo realiza el ndk-build. El fichero se encuentra en el NDK que se instalaró anteriormente, directamente en el Path principal. Para su uso, y dado que es un fichero Linux, se hizo uso de la herramienta Cygwin.

Cygwin es una herramienta para Windows que permite usar la BashShell (y otra Shell cualquiera) típica de UNIX en un entorno Windows. Puede obtenerse directamente desde:
Una vez descargado, procedimos a su instalación. Por defecto, nos instalará únicamente aquellas funcionalidades descritas como "default". Sin embargo, dado que en nuestro caso necesitaremos hacer un build de un código escrito en c/c++,  descargamos también todo lo relacionado con el apartado Dev (de development). Allí encontramos compiladores de c++ que utilizamos para compilar el código.
Una vez instalado y funcionando, escribimos en línea de comandos:

cd <path del jni>

para situarnos en donde tenemos el código escrito en c++.
Una vez allí, debimos encontrar y ejecutar directamente el ndk-build que encontramos en el path del ndk. En nuestro caso, éste se encontraba en:

C:\Development\android-ndk-r8d-windows\android-ndk-r8d\ndk-build

Una vez hecho esto, es muy común que nos aparezca el siguiente error:

<Path del ndk>/build/gmsl/__gmsl:512: *** non-numeric
second argument to `wordlist' function: ''.  Stop.

Esto es debido a una configuración no estandarizada del ndk-build. Para ello debimos hacer lo siguiente:
Buscamos en el path indicado el fichero llamado __gmsl. Allí, en la línea 512, donde se encuentra la función que nos da error con el segundo argumento, teníamos:

int_encode = $(__gmsl_tr1)$(wordlist 1, $1,$(__gmsl_input_int))

Como nos daba un error por ser éste segundo argumento no numérico, hicimos lo siguiente:
Cambiamos la declaración de la función por:

int_encode = $(__gmsl_tr1)$(wordlist 1,$(words $1),$
(__gmsl_input_int))

Guardamos el fichero y volvimos a ejecutar el ndk-build.
Una vez hecho esto, funcionó correctamente.
Así, con todo realizado correctamente, solo hizo falta cargar las librerías generadas en la carpeta "libs"
Para ello, dentro de nuestro código Java, solo faltaba cargar la librería con el Nombre que escogimos (sin el _jni). Para ello, escribimos:

static{
System.loadLibrary("Nombrede la librería");
}

y, más adelante en el código, declarar como nativos todos los métodos que vamos a usar:

private native double NombreMetodo(args)

No hay comentarios:

Publicar un comentario