Working with android JNI - java

I'm using a open source project in Gitgub.
I have to change package name of it.
Primary part of archArm.c function code source is :
jstring
Java_com_github_hiteshsondhi88_libffmpeg_ArmArchHelper_cpuArchFromJNI(JNIEnv* env, jobject obj)
And primary ArchArmHelper.java is :
package com.github.hiteshsondhi88.libffmpeg;
class ArmArchHelper {
static {
System.loadLibrary("ARM_ARCH");
}
native String cpuArchFromJNI();
boolean isARM_v7_CPU(String cpuInfoString) {
return cpuInfoString.contains("v7");
}
boolean isNeonSupported(String cpuInfoString) {
// check cpu arch for loading correct ffmpeg lib
return cpuInfoString.contains("-neon");
}
}
Now I edited part of archArm.c function to :
jstring
Java_com_emad_amerian_libffmpeg_ArmArchHelper_cpuArchFromJNI(JNIEnv* env, jobject obj)
And I edited my ArchArmHelper.java to :
package com.emad.amerian.libffmpeg;
class ArmArchHelper {
static {
System.loadLibrary("ARM_ARCH");
}
native String cpuArchFromJNI();
boolean isARM_v7_CPU(String cpuInfoString) {
return cpuInfoString.contains("v7");
}
boolean isNeonSupported(String cpuInfoString) {
// check cpu arch for loading correct ffmpeg lib
return cpuInfoString.contains("-neon");
}
}
I can to build my APK file successfully but when run Launch my project return error :
02-28 23:29:20.001 15094-15094/com.emad.amerian.sampleffmpeg E/art: No implementation found for java.lang.String com.emad.amerian.libffmpeg.ArmArchHelper.cpuArchFromJNI() (tried Java_com_emad_amerian_libffmpeg_ArmArchHelper_cpuArchFromJNI and Java_com_emad_amerian_libffmpeg_ArmArchHelper_cpuArchFromJNI__)

You need to export the function using the JNIEXPORT and JNICALL macros:
JNIEXPORT jstring JNICALL Java_com_emad_amerian_libffmpeg_ArmArchHelper_cpuArchFromJNI(JNIEnv* env, jobject obj)
This makes the function visible in the symbols table of the library so that the Java VM can link it.

Related

Need info regarding java.io.UnixFileSystem.getspace method, which unix command is executed?

Our Spring boot application hanged on production Unix environment. After getting the thread dump we were able to find that getSpace method of UnixFileSystem was locked. Can anyone help me with the Unix command that is called when java calls the getSpace method.
http-nio-8080-exec-1" - Thread t#45
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getSpace(Native Method)
at java.io.File.getUsableSpace(File.java:1885)
Basically a native call is made in java.io.UnixFileSystem to getSpace(...):
/* -- Disk usage -- */
public native long getSpace(File f, int t);
This native method ends up calling via JNI the method defined in this file of the JDK's repository:
https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/UnixFileSystem_md.c
In this file, at line 466, you find the following implementation (parts inside the if/else statements have been omitted for brevity):
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
#ifdef MACOSX
struct statfs fsstat;
#else
struct statvfs64 fsstat;
int res;
#endif
memset(&fsstat, 0, sizeof(fsstat));
#ifdef MACOSX
if (statfs(path, &fsstat) == 0) {
switch(t) {
// omitted
}
}
#else
RESTARTABLE(statvfs64(path, &fsstat), res);
if (res == 0) {
switch(t) {
// omitted
}
}
#endif
} END_PLATFORM_STRING(env, path);
return rv;
}
So as you can see, if you are using MACOSX the C library function statfs is called, otherwise the function statvfs64 is called.

Java JNI UnsatisfiedLinkError

I'm trying to expose C++ functionality to Java using JNI. I'm trying to get a simple message box to show up first, just to make sure everything works. However, I'm getting an UnsatisfiedLinkError exception being thrown (the exception is being thrown when I call the function, not)
Java class (project cpplib):
package src;
public class MessageBox {
static {
System.loadLibrary("cpplib");
}
private static native void libf_show(String message, String caption);
public static void show(String message, String caption) {
libf_show(message, caption);
}
}
Note: the folder of cpplib, the DLL, has been added into the native libraries path
C++ header messagebox.hpp:
#pragma once
#include "jni.h"
extern "C"
{
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption);
}
C++ source messagebox.cpp:
#include "messagebox.hpp"
#include <windows.h>
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption)
{
const char *message = env->GetStringUTFChars(jstr_message, 0);
const char *caption = env->GetStringUTFChars(jstr_caption, 0);
MessageBox(NULL, message, caption, MB_OK);
env->ReleaseStringUTFChars(jstr_message, message);
env->ReleaseStringUTFChars(jstr_caption, caption);
}
Full error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: src.MessageBox.libf_show(Ljava/lang/String;Ljava/lang/String;)V
at src.MessageBox.libf_show(Native Method)
at src.MessageBox.show(MessageBox.java:11)
at src.CPPLIB_Test.main(CPPLIB_Test.java:6)
I believe I'm using the 64-bit version of the JDK (as a 32-bit DLL didn't work), so I'm using the appropriate 64-bit JDK headers (if they are different at all).
Why doesn't Java like my DLL?
I tried to reproduce your problem but I get different header. If a recreate your MessageBox.java inside a src directory and compile with javac -d build src/MessageBox.java and finally obtain C/C++ headers with javah -d include -classpath build src.MessageBox
Then, I got this method signature
JNIEXPORT void JNICALL Java_src_MessageBox_libf_1show (JNIEnv *, jclass, jstring, jstring);
instead
JNIEXPORT void JNICALL Java_cpplib_src_MessageBox_show(JNIEnv *env, jstring jstr_message, jstring jstr_caption);
How are you creating your C++ header? maybe here it is the problem.

How to use .so library in android

I'm trying to use a python interpreter in my Android app to run SymPy. I already compiled Python for arm using this guide. http://mdqinc.com/blog/2011/09/cross-compiling-python-for-android/
This got me a libpython2.7.so file which I put into jniLibs/armeabi/.
In my app I load it as follows:
public class PythonTest {
static {
System.loadLibrary("python2.7");
}
public static native void Py_Initialize();
public static native void Py_Finalize();
public static native int PyRun_SimpleString(String s);
}
I am trying to use the methods from the headers located in the include directory which can also be found here: https://docs.python.org/2/c-api/
When I run the app on my device I get the following error:
No implementation found for void com.example.dorian.testapplication.PythonTest.Py_Initialize() (tried Java_com_example_dorian_testapplication_PythonTest_Py_1Initialize and Java_com_example_dorian_testapplication_PythonTest_Py_1Initialize__)
So to me this seems like the library loaded but it seems to look for the JNIEXPORT functions. But shouldn't I be able to use this library without writing specific C++ files? And if not, how would I accomplish this. Might there be a tool to generate wrapper files or something similar?
You need a JNI wrapper lib that will serve as a bridge between your Java code and libpython2.7.so. It may be enough for straters to have the three functions wrapped according to the JNI convention, e.g.
JNIEXPORT jint JNICALL com_example_dorian_testapplication_PythonTest_PyRun_1SimpleString
(JNIEnv *env, jclass jc, jstring js)
{
char* cs = env->GetStringUTFChars(js, 0);
std::string s = new std::string(cs);
env->ReleaseStringUTFChars(env, js, cs);
return PyRun_SimpleString(s.c_str());
}
If something is not clear, please read the tutorial at http://joaoventura.net/blog/2014/python-android-2.
Note that you can use any package name for the PythonTest class, not necessarily related to your Android app package name, e.g.
package com.python27;
class Python {
static {
System.loadLibrary("python2.7");
}
public static native void Initialize();
public static native void Finalize();
public static native int Run(String s);
}
will expect the JNI wrappers
JNIEXPORT void JNICALL com_python27_Python_Initialize(JNIEnv *env, jclass jc);
JNIEXPORT void JNICALL com_python27_Python_Finalize(JNIEnv *env, jclass jc);
JNIEXPORT jint JNICALL com_python27_Python_Run(JNIEnv *env, jclass jc, jstring js);

Why do I get UnsatisfiedLinkError when calling a MinGW-compiled function (not loading the library) on Windows?

I'm making a simple JNI test app using Eclipse on Windows. My C++ compiler is MinGW 4.6.2. Java is throwing an UnsatisfiedLinkError when I try to invoke a function in my test DLL (the DLL itself loads without problem). I have verified that my DLL exports a "C" function with the same name as the function generated by the javah utility.
How could trying to invoke the function possibly generate a link error? (Also, is there any way to get more detail as to what symbol isn't being found? A bald statement that there's an UnsatisfiedLinkError is next to useless.)
Here's the Java which defines the native function:
package com.xyz.jsdi_test;
import java.io.File;
public class JSDI
{
public static native void func(
String str,
int i,
Integer ii,
long j /* 64 bits */,
Long jj,
byte[] b
);
public static void dummy()
{
System.out.println("JSDI.dummy()");
}
static
{
File f = new File("..\\jsdi\\bin\\jsdi.dll");
System.out.println("Preparing to load: " + f);
System.load(f.getAbsolutePath());
System.out.println("Successfully loaded: " + f);
}
Here is the corresponding output from javah:
...
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xyz_jsdi_test_JSDI
* Method: func
* Signature: (Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
*/
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func
(JNIEnv *, jclass, jstring, jint, jobject, jlong, jobject, jbyteArray);
#ifdef __cplusplus
}
#endif
...And how I implemented the function...:
extern "C"
{
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func(
JNIEnv * env,
jclass _class,
jstring str,
jint i,
jobject ii,
jlong j,
jobject jj,
jbyteArray b
)
{
// don't do anything...let's just try to get called successfully...
}
} // extern "C"
Here is how I attempt to invoke it.
...
public static void main(String[] args)
{
JSDI.dummy(); // cause class to load, which should cause System.load() to run.
JSDI.func("hello", 0, 0, 0L, 0L, (byte[])null);
}
Finally, this is the output:
Preparing to load: ..\jsdi\bin\jsdi.dll
Successfully loaded: ..\jsdi\bin\jsdi.dll
JSDI.dummy()
java.lang.UnsatisfiedLinkError: com.xyz.jsdi_test.JSDI.func(Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
at com.xyz.jsdi_test.JSDI.func(Native Method)
at com.xyz.jsdi_test.SimpleTest.main(SimpleTest.java:24)
SOLVED IT -- WOOO!
It turns out that MSVC prepends an underscore to the name of __stdcall functions. MinGW does not. The Windows JVM apparently expects the '_' prefix. As soon as I prepended '_' to the function name and rebuilt with MinGW, everything worked just dandy.
eg:
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func
EDIT:
The --add-stdcall-underscore feature of the dlltool utility included with MinGW can solve this problem for you transparently. Set it up in your Makefile and you don't need to worry about having different versions of the actual source code for different compilers. See at this link.
Posting a working example, copy the content in the three files in the same directory (modify the path to your JDK) then invoke build.cmd
/* File: HelloWorld.java */
public class HelloWorld {
private static native void writeHelloWorldToStdout();
public static void main(String[] args) {
System.loadLibrary("HelloWorld");
writeHelloWorldToStdout();
}
}
/* File: HelloWorld.c */
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_writeHelloWorldToStdout(JNIEnv *env, jclass c)
{
printf("Hello World!");
}
rem File: build.cmd
%echo off
echo delete generated binaries
del HelloWorld.class
del HelloWorld.dll
del HelloWorld.h
del HelloWorld.def
echo Compile the Java Class
javac HelloWorld.java
echo Generate the Header file
javah -classpath . -o HelloWorld.h HelloWorld
echo Build the DLL
gcc -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include\win32" -Wl,--add-stdcall-alias -Wl,--output-def,HelloWorld.def -shared -o HelloWorld.dll HelloWorld.c
echo run the program
java HelloWorld
The signature in the exception doesn't have the 'int' parameter. So your Java code disagrees with your native code.

Calling Android NDK function from Unity Script

So I'm creating an Android app that uses Unity ... I am getting some assetbundle from Unity but I do not know the url before Unity starts. Thus Unity needs to call a function on the Native (Android) side to get the url.
I have been lost for awhile on how to do this (the Unity documentation is quite terrible imho). I decided to use the NDK to help me out. Without Unity everything about my library file works out... now the issue is calling these C functions in Unity.
Here is my lib:
#include <jni.h>
#include <string.h>
#include <android/log.h>
#define DEBUG_TAG "NDK_blahy"
extern "C" {
jstring Java_com_blah_blah_getURL(JNIEnv * env, jobject this);
void Java_com_blah_blah_setURL(JNIEnv * env, jobject this, jstring URL);
}
jstring url;
void Java_com_blah_blah_setURL(JNIEnv * env, jobject this, jstring URL)
{
url = URL;
jboolean isCopy;
const char * szLogThis = (*env)->GetStringUTFChars(env, URL, &isCopy);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", szLogThis);
(*env)->ReleaseStringUTFChars(env, URL, szLogThis);
}
jstring Java_com_lyfelotto_blah_blah_getURL(JNIEnv * env, jobject this)
{
return url;
}
My unity code loads the library just fine (using [DllImport ("libname")]).
Now, if I load the function "correctly" like this private static extern jstring Java_com_lyfelotto_blah_blah_getURL(JNIEnv * env, jobject this) bad things happen
Have I gone about this the wrong way? (Like I said, all I need to do is get a string). Any ideas?
I suggest ditch the NDK. Use the Android java plugin.
In YourPlugin.cs:
using UnityEngine;
using System.Collections;
using System.IO;
#if UNITY_ANDROID
public class UnityUrlPlugin {
// Logs the user out and invalidates the token
public static string getUrl() {
if( Application.platform != RuntimePlatform.Android )
return;
var pluginClass = new AndroidJavaClass("com.you.UnityUrlPlugin") ;
AndroidJavaObject plugin = pluginClass.CallStatic<AndroidJavaObject>("instance");
return plugin.Call<string>("getURL");
}
}
#endif
Then, in UnityUrlPlugin.java:
package com.you;
import android.content.ContentValues;
import android.content.Intent;
import android.os.Environment;
public class UnityUrlPlugin {
private static UnityUrlPlugin m_instance;
public static UnityUrlPlugin instance() {
if(m_instance == null)
m_instance = new UnityUrlPlugin();
return m_instance;
}
private UnityUrlPlugin(){
}
public String getURL() {
return "http://blah.com";
}
}
And throw UnityUrlPlugin.jar in to Assets/Plugins/Android folder.
No need for NDK!
You could likely send the 'dynamic' url in your call to getURL. Do something like:
return plugin.Call<string>("getURL", new object[] {"http://thisIsMyUrl.com"});
and then your java looking like this:
public String getURL(Object newUrl) {
return newUrl.ToString();
}

Categories