I have some code written in c++ by android JNI.
in one export function
"extern "C" JNIEXPORT int JNICALL Java_com_android_mymapsforge_create(JNIEnv *env, jobject obj)
{
...
ifstream infile("C:\\Users\\li\\Documents\\Visual Studio 2010\\Projects\\importANN\\Debug\\nodes.csv");
string line;
int index = 0;
while( getline( infile, line ))
{
....
}
}
when i using those original c++ code, i test under vs2010. Either i assign the infile() parameters with a fixed file path, or using some c++ get_file_path function, it's a thing related windows system, there is no problem. both work.
But now I want to using these code under JNI using by android. So the file path system change to android and some function need be adjusted.
My problem arise: because the code is using at last by my android java code under a android app, so the "C:\Users\li\Documents\Visual Studio 2010\Projects\importANN\Debug\nodes.csv", no longer take effect, it may be change to "/mnt/sdcard/somefolder/nodes.csv"
but i have experience when i in android app give a fixed file path it tell my some error, so under android jave envi, i using below code to get file path, similar code here:
String mpath ;
mpath = Environment.getExternalStorageDirectory().getPath();
mapView.setMapFile(new File(mpath+"/"+"berlin.map"));
(omit the filename , i just want to tell the method i use)
But these android code must can not use in my c++ wrapping function Java_com_android_mymapsforge_create,
So my question is :
how to cope with the file(android system) path things under the c++ export function? For example, i put the "nodes.csv" under my sdcard root folder, and want to do some processing under the JNI exported c++ function?
The easiest way is to pass the path as an argument of the function.
extern "C" JNIEXPORT int
JNICALL Java_com_android_mymapsforge_create(JNIEnv *env, jobject, jstring path)
Then you can turn jstring path to char* using JNI converting jstring to char *.
The second way is to call Android Environment.getExternalStorageDirectory().getPath() from JNI function like described here - Call a static java method of another package from native code.
Related
I have a situation of a C++ executable which creates a JVM using JNI_CreateJavaVM, and that java method wants to invoke a native method back:
//In Java:
private native void myNativeMethod();
//In C++:
JNIEXPORT void JNICALL
Java_SomeClass_myNativeMethod(JNIEnv *env, jobject instance) {
..
}
But it doesn't work (linking error).
BUT, if I move the method into a c++ library and load the library from Java using System.loadLibrary, it does work.
Any way of enabling this behaviour with only a c++ executable without going through the trouble of having 3 pieces (main c++ executable => starting a JVM and running a jar => loading a c++ library) but instead keeping it at two?
Thanks!
When loading a native library in Java using System.loadLibrary, it is stored internally in a list that is attached to the current ClassLoader, and then this list of libraries is searched when looking up the native method. So, it will indeed not see the native method you have defined in the same executable.
You should be able to make this work using the RegisterNatives JNI API, which allows explicitly registering native functions for a particular class as pointers.
JNIEnv env = ...
jclass cSomeClass = env->FindClass("SomeClass");
JNINativeMethod natives[] = {
{(char*) "myNativeMethod", (char*) "()V", (void*) &Java_SomeClass_myNativeMethod},
};
env->RegisterNatives(cSomeClass, natives, 1);
I created a JNI folder in my APP in Android Studio, I copied all my C files, but I need to call only one: NeuralNetwork.c, there are also NeuralNetwork_initialize.c and NeuralNetwork_terminate.c. I already have my CMakeFile.
I know I need to create another C file with something like this (it's called main.c):
#import <jni.h>
JNIEXPORT jstring JNICALL Java_com_dsp_testapp_NewRecordingActivity_getString(JNIEnv *env, jobject thiz) {
return (*env)->NewStringUTF(env, "Hello World from C!");
}
But, I need it to call specifically my NeuralNetwork.c, I know also I have to use the initialize and terminate somehow, but don't really know how.
Now, in my Java class I did this:
System.loadLibrary("my_src");
my_src is the name of my library generated with my CMake file.
But I don't know how to call then my main.c function.
NeuralNetwork.c gives me just a double: 1, or 0 and has 5 vector inputs with length of 256.
Could you please help me with this?
You basically need to create a Java class with the qualified name (class + package) corresponding to what you used in your library (com.dsp.testapp.NewRecordingActivity). Then you need to declare the method as native:
public native getString(Object obj)
Afterwards, just create an object of this class and call the method.
I have made a program in Java that calls to some functions in native language C. Made a shared library of that C function file and made a shared library, and all worked perfectly.
My problem is when I try to call other functions for example in PBC (Pairing Based Cryptography) library. The C files that are in the shared library include the required .h files for knowing the functions in PBC but I can't use them, I don't know why. What should I do? How can I call functions that are in another libraries?
Java code for loading the libraries.
static {
System.loadLibrary("myLibrary");
System.loadLibrary("pbc");
}
Error when executing my own Java program:
undefined symbol: pairing_init_set_buf
Make sure to link your JNI code with shared library you want to use.
You can take a look at sample code here:
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo023
In this sample you have JNI function:
JNIEXPORT void JNICALL Java_recipeNo023_HelloWorld_displayMessage
(JNIEnv *env, jclass obj) {
printf("Hello world!\n");
/* We are calling function from another source */
anotherFunction();
}
that calls function from some external shared library
void anotherFunction() {
// we are printing message from another C file
printf("Hello from another function!\n");
}
You have to make sure that your JNI library is linked with the library you want to use:
cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(ARCH) c/recipeNo023_HelloWorld.c -L./lib -lAnotherFunction -o lib/libHelloWorld.$(EXT)
In this sample
-L./lib -lAnotherFunction
tells compiler to use this "other" library that contains symbols not available inside library that contains JNI code.
Android with NDK has support to C/C++ code and iOS with Objective-C++ has support too, so how can I write applications with native C/C++ code shared between Android and iOS?
Update.
This answer is quite popular even four years after I write it, in this four years a lot of things has changed, so I decided to update my answer to fit better our current reality. The answer idea does not change; the implementation has changed a little. My English also has changed, it has improved a lot, so the answer is more understandable to everyone now.
Please take a look at the repo so you can download and run the code I'll show below.
The Answer
Before I show the code, please take a lot on the following diagram.
Each OS has its UI and peculiarities, so we intend to write specific code to each platform in this regard. In other hands, all logic code, business rules, and things that can be shared we intend to write using C++, so we can compile the same code to each platform.
In the diagram, you can see the C++ layer at the lowest level. All shared code is in this segment. The highest level is regular Obj-C / Java / Kotlin code, no news here, the hard part is the middle layer.
The middle layer to iOS side is simple; you only need to configure your project to build using a variant of Obj-c know as Objective-C++ and it is all, you have access to C++ code.
The thing became harder on the Android side, both languages, Java and Kotlin, on Android, run under a Java Virtual Machine. So the only way to access C++ code is using JNI, please take time to read the basics of JNI. Fortunately, today's Android Studio IDE has vast improvements on JNI side, and a lot of problems are shown to you while you edit your code.
The code by steps
Our sample is a simple app that you send a text to CPP, and it converts that text to something else and returns it. The idea is, iOS will send "Obj-C" and Android will send "Java" from their respective languages, and the CPP code will create a text as a follow "cpp says hello to << text received >>".
Shared CPP code
First of all, we are going to create the shared CPP code, doing it we have a simple header file with the method declaration that receives the desired text:
#include <iostream>
const char *concatenateMyStringWithCppString(const char *myString);
And the CPP implementation:
#include <string.h>
#include "Core.h"
const char *CPP_BASE_STRING = "cpp says hello to %s";
const char *concatenateMyStringWithCppString(const char *myString) {
char *concatenatedString = new char[strlen(CPP_BASE_STRING) + strlen(myString)];
sprintf(concatenatedString, CPP_BASE_STRING, myString);
return concatenatedString;
}
Unix
An interesting bonus is, we can also use the same code for Linux and Mac as well as other Unix systems. This possibility is especially useful because we can test our shared code faster, so we are going to create a Main.cpp as follow to execute it from our machine and see if the shared code is working.
#include <iostream>
#include <string>
#include "../CPP/Core.h"
int main() {
std::string textFromCppCore = concatenateMyStringWithCppString("Unix");
std::cout << textFromCppCore << '\n';
return 0;
}
To build the code, you need to execute:
$ g++ Main.cpp Core.cpp -o main
$ ./main
cpp says hello to Unix
iOS
It is time to implement on the mobile side. As far as iOS has a simple integration we are starting with it. Our iOS app is a typical Obj-c app with only one difference; the files are .mm and not .m. i.e. It is an Obj-C++ app, not an Obj-C app.
To a better organization, we create the CoreWrapper.mm as follow:
#import "CoreWrapper.h"
#implementation CoreWrapper
+ (NSString*) concatenateMyStringWithCppString:(NSString*)myString {
const char *utfString = [myString UTF8String];
const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
NSString *objcString = [NSString stringWithUTF8String:textFromCppCore];
return objcString;
}
#end
This class has the responsibility to convert CPP types and calls to Obj-C types and calls. It is not mandatory once you can call CPP code on any file you want on Obj-C, but it helps to keep the organisation, and outside your wrapper files you maintain a complete Obj-C styled code, only the wrappers file become CPP styled.
Once your wrapper is connected to the CPP code, you can use it as a standard Obj-C code, e.g. ViewController"
#import "ViewController.h"
#import "CoreWrapper.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *label;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString* textFromCppCore = [CoreWrapper concatenateMyStringWithCppString:#"Obj-C++"];
[_label setText:textFromCppCore];
}
#end
Take a look of how the app looks:
Android
Now it is time for Android integration. Android uses Gradle as the build system, and to C/C++ code it uses CMake. So the first thing we need to do is to configure the CMake on gradle file:
android {
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
...
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++14"
}
}
...
}
And the second step is to add the CMakeLists.txt file:
cmake_minimum_required(VERSION 3.4.1)
include_directories (
../../CPP/
)
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
../../CPP/Core.h
../../CPP/Core.cpp
)
find_library(
log-lib
log
)
target_link_libraries(
native-lib
${log-lib}
)
The CMake file is where you need to add the CPP files and header folders you will use on the project, on our example, we are adding the CPP folder and the Core.h/.cpp files. To know more about C/C++ configuration please read it.
Now the core code is part of our app it is time to create the bridge, to make the things more simple and organized we create a specific class named CoreWrapper to be our wrapper between JVM and CPP:
public class CoreWrapper {
public native String concatenateMyStringWithCppString(String myString);
static {
System.loadLibrary("native-lib");
}
}
Note this class has a native method and loads a native library named native-lib. This library is the one we create, in the end, the CPP code will become a shared object .so File embed in our APK, and the loadLibrary will load it. Finally, when you call the native method, the JVM will delegate the call to the loaded library.
Now the most strange part of Android integration is the JNI; We need a cpp file as follow, in our case "native-lib.cpp":
extern "C" {
JNIEXPORT jstring JNICALL Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString(JNIEnv *env, jobject /* this */, jstring myString) {
const char *utfString = env->GetStringUTFChars(myString, 0);
const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
jstring javaString = env->NewStringUTF(textFromCppCore);
return javaString;
}
}
The first thing you will notice is the extern "C" this part is necessary to JNI work correctly with our CPP code and method linkages. You will also see some symbols JNI uses to works with JVM as JNIEXPORT and JNICALL. To you understand the meaning of those things, it is necessary to take a time and read it, for this tutorial purposes just consider these things as boilerplate.
One significant thing and usually the root of a lot of problems is the name of the method; it needs to follow the pattern "Java_package_class_method". Currently, Android studio has excellent support for it so it can generate this boilerplate automatically and show to you when it is correct or not named. On our example our method is named "Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString" it is because "ademar.androidioscppexample" is our package, so we replace the "." by "_", CoreWrapper is the class where we are linking the native method and "concatenateMyStringWithCppString" is the method name itself.
As we have the method correctly declared it is time to analyze the arguments, the first parameter is a pointer of JNIEnv it is the way we have access to JNI stuff, it is crucial to we make our conversions as you will see soon. The second is a jobject it is the instance of the object you had used to call this method. You can think it as the java "this", on our example we do not need to use it, but we still need to declare it. After this jobject, we are going to receive the arguments of the method. Because our method has only one argument - a String "myString", we have only a "jstring" with the same name. Also notice that our return type is also a jstring. It is because our Java method returns a String, for more information about Java/JNI types please read it.
The final step is to convert the JNI types to the types we use on CPP side. On our example, we are transforming the jstring to a const char * sending it converted to the CPP, getting the result and converting back to jstring. As all other steps on JNI, it is not hard; it is only boilerplated, all the work is done by the JNIEnv* argument we receive when we call the GetStringUTFChars and NewStringUTF. After it our code is ready to run on Android devices, lets take a look.
Approach described in the excellent answer above can be completely automated by Scapix Language Bridge which generates wrapper code on the fly directly from C++ headers. Here is an example:
Define your class in C++:
#include <scapix/bridge/object.h>
class contact : public scapix::bridge::object<contact>
{
public:
std::string name();
void send_message(const std::string& msg, std::shared_ptr<contact> from);
void add_tags(const std::vector<std::string>& tags);
void add_friends(std::vector<std::shared_ptr<contact>> friends);
};
And call it from Swift:
class ViewController: UIViewController {
func send(friend: Contact) {
let c = Contact()
contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
}
}
And from Java:
class View {
private contact = new Contact;
public void send(Contact friend) {
contact.sendMessage("Hello", friend);
contact.addTags({"a","b","c"});
contact.addFriends({friend});
}
}
How to change and update the title of the command prompt window from the java command line application? Every time I run my application, the command prompt window title shows:
C:\WINDOWS\system32\cmd.exe - java MyApp.
I'd like to change and update the window title as the java program runs, for example as wget(win32) updates downloading status in the title: Wget [12%].
Although I haven't tried it myself, in Windows, one can use the Win32 API call to SetConsoleTitle in order to change the title of the console.
However, since this is a call to a native library, it will require the use of something like Java Native Interface (JNI) in order to make the call, and this will only work on Windows 2000 and later.
Edit - A solution using JNI
The following is an example of using JNI in order to change the title of the console window from Java in Windows. To implement this, the prerequiste is some knowledge in C and using the compiler/linker.
First, here's result:
(source: coobird.net)
Disclaimer: This is my first Java application using JNI, so it's probably not going to be a good example of how to use it -- I don't perform any error-checking at all, and I may be missing some details.
The Java program was the following:
class ChangeTitle {
private static native void setTitle(String s);
static {
System.loadLibrary("ChangeTitle");
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 5; i++) {
String title = "Hello! " + i;
System.out.println("Setting title to: " + title);
setTitle(title);
Thread.sleep(1000);
}
}
}
Basically, the title is changed every 5 seconds by calling the setTitle native method in an external native library called ChangeTitle.
Once the above code is compiled to make a ChangeTitle.class file, the javah command is used to create a C header that is used when creating the C library.
Writing the native library
Writing the library will involve writing the C source code against the C header file generated by javah.
The ChangeTitle.h header was the following:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ChangeTitle */
#ifndef _Included_ChangeTitle
#define _Included_ChangeTitle
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ChangeTitle
* Method: setTitle
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_ChangeTitle_setTitle
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
Now, the implementation, ChangeTitle.c:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <jni.h>
#include "ChangeTitle.h"
JNIEXPORT void JNICALL
Java_ChangeTitle_setTitle(JNIEnv* env, jclass c, jstring s) {
const jbyte *str;
str = (*env)->GetStringUTFChars(env, s, NULL);
SetConsoleTitle(str);
(*env)->ReleaseStringUTFChars(env, s, str);
};
A String that is passed into the native function is changed into an UTF-8 encoded C string, which is sent to the SetConsoleTitle function, which, as the function name suggests, changes the title of the console.
(Note: There may be some issues with just passing in the string into the SetConsoleTitle function, but according to the documentation, it does accept Unicode as well. I'm not too sure how well the code above will work when sending in various strings.)
The above is basically a combination of sample code obtained from Section 3.2: Accessing Strings of The Java Native Interface Programmer's Guide and Specification, and the SetConsoleTitle Function page from MSDN.
For a more involved sample code with error-checking, please see the Section 3.2: Accessing Strings and SetConsoleTitle Function pages.
Building the DLL
The part that turned out to take the most amount of time for me to figure out was getting the C files to compile into an DLL that actually could be read without causing an UnsatisfiedLinkError.
After a lot of searching and trying things out, I was able to get the C source to compile to a DLL that could be called from Java. Since I am using MinGW, I found a page form mingw.org which described exactly how to build a DLL for JNI.
Sources:
The Java Native Interface Programmer's Guide and Specification
Chapter 2: Getting Started - Details the process using JNI.
JNI-MinGW-DLL - Building a JNI DLL on MinGW with gcc.
This depends on your terminal emulator, but essentially it's just printing out control sequences to the console.
Now I'm not clear on what control sequences CMD.EXE responds to (I haven't one available to try this on) but I hear there's a command called TITLE which sets the title of the window. I tried piping TITLE's output to a file, but apparently, it doesn't actually set the title by outputting control characters. The START command can take a parameter which is title of the window followed by the command to run in the window. So something like
cmd TITLE "lovely Application that is in a command window." && "java" MyApp
REM or
start "lovely Application that is java based." java MyApp
Personally I would just bundle the whole thing with a shortcut where you can edit the properties such as the current directory, the command, it's parameters, and the window size, style and title (if I remember rightly). Give it a nice icon and people will use it.
Here's my solution using JNA:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class SetTitle {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "kernel32" : "c"),
CLibrary.class);
boolean SetConsoleTitleA(String title);
}
public static void main(String[] args) {
CLibrary.INSTANCE.SetConsoleTitleA("Testing 123");
System.exit(0);
}
}
You can use the CLITools Java library
following dlamblin's revelation ;-)
here's a python code.
note that there are 2 different commands in most programming languages:
system
exec
system will issue a system command, exec indeed spawns a new process. thus:
C:\>python
>>> import os
>>> os.system("title berry tsakala")
which works inside a running program. Just find the java equivalent.