I have a .cpp file to use with java on android:
#include<iostream>
#include<jni.h>
jint Java_com_example_gatsj_tutorjatek_MainActivity_Sum(JNIEnv* env, jobject obj)
{
return 5;
}
I use it here:
package com.example.gatsj.tutorjatek;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
public native int Sum();
static
{
System.loadLibrary("TestCPP");
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
int x = Sum();//IF I REMOVE THIS LINE THE APP DOESN'T CRASH
}
}
I build it in Android Studio using Gradle and this CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
TestCPP
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/TestCPP.cpp )
When I start the app on my phone it crashes. But if I remove the "int x = Sum();" line, the app can start.
The "loadLibrary" and "native" method part is still in the code but without the "int x = Sum();" line, the app doesn't crash.
How can I use the Sum() method? What causes the problem?
Since C++ is being used instead of C, you should wrap the defination of your native methods inside extern "C" in your cpp file.
extern "C" {
// your native method definations.
}
Related
Am trying to call a x86 DLL that I created using VC 6, from a java project on Eclipse, first try I got an error saying that I can't call a x86 DLL from a x64 envirement and that the DLL can't be loaded. So I installed a x86 jre and I have no more problem to charge the DLL.
But when I try to call my c++ function I get the following exception:
Exception in thread "main" java.lang.UnsatisfiedLinkError: mm.SimpleDLL.SimpleDLL_Calculation_Add(II)I
Can someone please help me ?
Thank you.
SimpleDLL.h
#ifndef SIMPLE_DLL_H
#define SIMPLE_DLL_H
namespace SimpleDll
{
extern class Calculation
{
public:
static __declspec(dllexport) int Add(int a, int b);
};
}
#endif SIMPLE_DLL_H
SimpleDLL.cpp
#include "SimpleDll.h"
namespace SimpleDll
{
int Calculation::Add(int a, int b)
{ return a + b; }
}
SimpleDLL.java
package mm;
public class SimpleDLL {
static
{
System.load("D:\\SimpleDLL.dll");
}
public static void main(String ar[])
{
System.out.println("Hello world from Java");
SimpleDLL t=new SimpleDLL();
int x = t.SimpleDLL_Calculation_Add(6, 7);
System.out.println("Resultat "+x);
}
public native int SimpleDLL_Calculation_Add(int a, int b);
}
Exported DLL Functions View
Resolved using the JNA library, I used this link to walkthrough
You need to build a 32-bit DLL and a .h file using javah for the exact signature Java expects. It is usually something like
JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
(JNIEnv *, jobject, jboolean);
from https://medium.com/#bschlining/a-simple-java-native-interface-jni-example-in-java-and-scala-68fdafe76f5f
An alternative approach is to use a library like JNA or JNR-FFI which allow you to bind to a C library without writing this bridging code.
Try to create a java class from c++ side but keep crashing
appBucksAdv.java
package adv;
import org.qtproject.qt5.android.bindings.QtApplication;
import org.qtproject.qt5.android.bindings.QtActivity;
import android.os.Bundle;
public class appBucksAdv extends QtActivity
{
}
simpleJNI.hpp
#include <QObject>
#include <QtAndroidExtras/QAndroidJniObject>
class simpleJNI : public QObject
{
Q_OBJECT
public:
explicit simpleJNI(QObject *parent = nullptr);
private:
QAndroidJniObject advClass_;
};
simpleJNI.cpp
#include "simpleJNI.hpp"
simpleJNI::simpleJNI(QObject *parent) :
QObject(parent),
advClass_("adv/appBucksAdv") //crash when I create the java class
{
}
error messages :
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtMessageDialogHelper'
#
D/dalvikvm(21397): Trying to load lib /data/data/org.qtproject.example.androidTest/lib/libQt5QuickParticles.so 0x4176efd0
#
D/dalvikvm(21397): Shared lib '/data/data/org.qtproject.example.androidTest/lib/libQt5QuickParticles.so' already loaded in same CL 0x4176efd0
#
D/dalvikvm(21397): Trying to load lib /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0
#
D/dalvikvm(21397): Added shared lib /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0
#
D/dalvikvm(21397): No JNI_OnLoad found in /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0, skipping init
#
W/Qt (21397): kernel/qcoreapplication.cpp:416 (QCoreApplicationPrivate::QCoreApplicationPrivate(int&, char**, uint)): WARNING: QApplication was not created in the main() thread.
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtNativeInputConnection'
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtExtractedText'
1.full messages
2.codes
Are you loading the correct Activity? you need to edit the manifest to make it load your custom activity. You should also avoid calling the constructor of the QtActivity class yourself (advClass_("adv/appBucksAdv")), as it will attempt to prepare the application again. What you probably want is a handle to the activity and since you are already extending the QtActivity class, just add a static function that returns a handle to it.
E.g.:
Java:
public class appBucksAdv extends QtActivity
{
...
static appBucksAdv getActivity() { return thisActivity; }
...
}
C++
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("adv/appBucksAdv",
"getActivity",
"()Ladv/appBucksAdv;");
We currently have some image processing software written in c++ which is being used by our IOS application. I am trying to integrate this image processing code into the android project that I created using the Android NDK.
I have my android project and the sdk all setup and ready to go. I also have the ndk setup and ready to go.
I was following through on this tutorial (which is awesome), and I got stumped at the part that he defined the code for native.c because it had a function name like this,
void Java_com_mamlambo_sample_ndk1_AndroidNDK1SampleActivity_helloLog(JNIEnv * env, jobject this, jstring logThis)
It almost looks to me like I have to go through all of my existing c++ functions and alter the code in order for the NDK to recognize it.
So here are my questions,
Do I have to alter my existing c++ code in order for it to work with the ndk builder? And if so, what are the things I need to change in my code for this work?
Is there a way to have the Android.mk file build an entire directory? I have a lot of files and I did not want to have to list out every single one of them in order to get them built.
1) You should be able to build without alteration, but you will need to write some of those JNI wrapper functions to call it from Java. Hopefully you have a small number of top-level classes and you will only need to wrap those. E.g. Here's what I have for a game I'm (slowly) writing:
// android.cpp
#include "game.h"
#include <jni.h>
namespace {
Game* toGame(jlong gamePtr) {
return reinterpret_cast<Game*>(gamePtr);
}
}
extern "C" {
jlong Java_com_rarepebble_game3_Game_create(JNIEnv* env, jobject jobj) {
Game* g = new Game();
return reinterpret_cast<jlong>(g);
}
void Java_com_rarepebble_game3_Game_destroy(JNIEnv* env, jobject jobj, jlong gamePtr) {
delete toGame(gamePtr);
}
void Java_com_rarepebble_game3_Game_update(JNIEnv* env, jobject jobj, jlong gamePtr, jboolean isTouching, jfloat touchX, jfloat touchY) {
toGame(gamePtr)->update(isTouching, touchX, touchY);
}
void Java_com_rarepebble_game3_Game_render(JNIEnv* env, jobject jobj, jlong gamePtr) {
toGame(gamePtr)->render();
}
// ... and a few others. Only one class, though.
}
On the Java side this lets me declare those functions in my com.rarepebble.game3.Game class and call them at the appropriate times in the app's lifecycle. (Note how the Java package, class and function names correspond to the function names in C++):
//Game.java
package com.rarepebble.game3;
// (imports)
public class Game {
static {
System.loadLibrary("game3lib");
}
// These are the functions you defined in C++
private native long create();
private native void destroy(long gamePtr);
private native void update(long gamePtr, boolean isTouched, float x, float y);
private native void render(long gamePtr);
private long gamePtr = 0;
Game() {
gamePtr = create();
}
#Override
protected void finalize() throws Throwable {
if (gamePtr != 0) {
destroy(gamePtr);
}
super.finalize();
}
// etc...
}
2) You can use
LOCAL_SRC_FILES := $(wildcard *.cpp) $(wildcard subdirectory/*.cpp) #etc...
Edit: As requested, my C++ Game class header, "game.h":
// game.h
#ifndef GAME_INCLUDED
#define GAME_INCLUDED
// Various game and standard library includes.
// *NO* JNI or Android includes.
namespace game {
class Game {
public:
Game();
~Game();
void update(bool isTouching, float touchX, float touchY);
void render();
// other funcs...
private:
// etc...
};
}
#endif //def GAME_INCLUDED
Similarly, "game.cpp" doesn't include any JNI stuff either.
For some reason I can only call native functions from my main activity and not any custom views that I've created. Here is an example file (I followed a tutorial, but renamed the classes http://mindtherobot.com/blog/452/android-beginners-ndk-setup-step-by-step/)
See the usage of the native function "getNewString".
package com.example.native;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.View;
public class NativeTestActivity extends Activity
{
static
{
System.loadLibrary("nativeTest");
}
private native String getNewString();
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(new BitmapView(this));
String hello = getNewString(); // This line works fine
new AlertDialog.Builder(this).setMessage(hello).show();
}
}
class BitmapView extends View
{
static
{
System.loadLibrary("nativeTest");
}
private native String getNewString();
public BitmapView(Context context)
{
super(context);
String hello = getNewString(); // This line throws the UnsatisfiedLinkError
new AlertDialog.Builder(this.getContext()).setMessage(hello).show();
}
}
How can I call native functions in my custom views?
I've built the application as an Android 2.2 app. I'm running the application on my HTC Desire. I have the latest SDK (9) and latest NDK (r5).
Your problem is that you are trying to call the native function from a class where it dont belongs to.
You defined the following JNI function in your c file:
jstring Java_com_example_native_NativeTestActivity_getNewString()
This states that the native function when loaded will bind with the method declared as native in NativeTestActivity class. So when you try to call it from your View class it doesn't find any function to bind to.
In that case it will look for the following function (which of course does not exist in your .so):
jstring Java_com_example_native_BitmapView_getNewString()
If you still want to be able to call the same function from different classes you can declare it in a container class that can be accessed from any class you want.
eg:
java code:
package com.example.native;
public class NativeHelper {
public native String getNewString();
static
{
System.loadLibrary("nativeTest");
}
}
c code:
jstring Java_com_example_native_NativeHelper_getNewString(JNIEnv* env, jobject javaThis)
{
return (*env)->NewStringUTF(env, "Hello from native code!");
}
I'm trying to bind the dhcpctl library to Java using JNA. This is mi code (I didn't declare all the functions yet):
package com.abiquo.abicloud.omapi;
import com.abiquo.abicloud.omapi.DHCPControlStructure.DHCPCtrlDataString;
import com.abiquo.abicloud.omapi.DHCPControlStructure.DHCPHandle;
import com.abiquo.abicloud.omapi.OmapiControlStructure.OmapiObjectTypeT;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
/**
* Binding of the dhcpctl header.
* #author jdevesa#abiquo.com
*/
public interface DHCPControlLibrary extends Library
{
/**
* Create the loaded instance of the native library
*/
DHCPControlLibrary INSTANCE =
(DHCPControlLibrary) Native.loadLibrary("dhcpctl", DHCPControlLibrary.class);
/**
* Define as synchronized
*/
DHCPControlLibrary SYNC_INSTANCE=(DHCPControlLibrary) Native.synchronizedLibrary(INSTANCE);
int dhcpctl_initialize ();
int dhcpctl_connect (DHCPHandle handle1, String address, int port, DHCPHandle.ByValue handle2);
int dhcpctl_wait_for_completion (DHCPHandle handle, Pointer int1);
int dhcpctl_get_value (DHCPCtrlDataString dataString , DHCPHandle.ByValue handleValue, String str1);
int dhcpctl_get_boolean (Pointer int1, DHCPHandle.ByValue handleValue, String str1);
int dhcpctl_set_value (DHCPHandle.ByValue handleValue, DHCPCtrlDataString dataString, String str1);
... etc ...
}
dhcpctl uses omapi library to call the remote DHCP server. So, when I try to load the library with:
DHCPControlLibrary dhcpExecutor = DHCPControlLibrary.INSTANCE;
it throws the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'dhcpctl': /usr/lib/libdhcpctl.so: undefined symbol: omapi_type_generic
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:160)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:228)
at com.sun.jna.Library$Handler.<init>(Library.java:140)
at com.sun.jna.Native.loadLibrary(Native.java:372)
at com.sun.jna.Native.loadLibrary(Native.java:357)
at com.abiquo.abicloud.omapi.DHCPControlLibrary.<clinit>(DHCPControlLibrary.java:40)
at com.abiquo.abicloud.omapi.DHCPexecution.main(DHCPexecution.java:11)
omapi__type__generic is an external variable stored in omapi.h. I think I have to do a sort of linking when i load the library, but I don't know how to do it.
Many thanks.
omapi_type_generic is not "an external variable stored in omap.h".
This variable must be defined in some .c file somewhere and hence in some .so or .a.
If it is not defined in any .c file then there is your problem right there. Find out why it is so and fix it and you should overcome this exception.
I think u forget extern "C" while writing c++ code.
In my case
c++ code:
#include <stdlib.h>
#include <iostream>
using namespace std;
extern "C"
{
void test() {
cout << "TEST" << endl;
}
int addTest(int a,int b)
{
int c = a + b ;
return c ;
}
}
and java code
import com.sun.jna.Library;
import com.sun.jna.Native;
public class jnatest1 {
public interface Clibrary extends Library {
Clibrary INSTANTCE = (Clibrary) Native.loadLibrary("hello",
Clibrary.class);
void test();
int addTest(int a,int b);
}
public static void main(String[] args) {
Clibrary.INSTANTCE.test();
int c = Clibrary.INSTANTCE.addTest(10,20);
System.out.println(c);
}
}
it works for me
Most likely you will need to either explicitly load the omapi library or ensure that it is in LD_LIBRARY_PATH so that the system can find it automatically when the dhcpctl library is loaded.