Working in android studio ndk gameactivity. I have a C++ class with a "score" integer saved. From my MainActivity.kt, I would like to be able to retrieve this value.
currently my MainActivity.kt has a
external fun getScore() : Int
in my .cpp file I have a
extern "C"
{
JNIEXPORT jint JNICALL Java_com_example_mgl_MainActivity_getScore(JNIEnv *env, jobject thiz) {
//Access C++ class here somehow
//Call a function in the class to return the score
return 0;
}
}
Related
I have a C++ library with objects and am using this in conjunction with Java over JNI. Ideally, I'd like to tie the life cycle of the C++ objects to their Java equivalents and make it controllable from Java. Important to note is that the application is multi-threaded, so the C++ objects are accessed from multiple threads, and that those objects aren't modified after their initial construction. My current implementation looks something like this:
struct Tree {
int age = 0;
std::string type;
}
typedef Tree* TreeHandle;
/*
* Class: com_example_Tree
* Method: initialize
* Signature: ([Lcom/example/Tree;)I
*/
JNIEXPORT jlong JNICALL Java_com_example_Tree_initialize
(JNIEnv * env, jclass cls, jobject j_tree) {
auto tree_ptr = new Tree();
return (jlong) tree_ptr;
}
/*
* Class: com_example_Tree
* Method: dispose
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_Tree_dispose
(JNIEnv * env, jclass cls, jlong j_tree_handle) {
auto tree_ptr = reinterpret_cast<TreeHandle*>(j_tree_handle);
delete tree_ptr;
}
My Java part then looks like this:
class Tree implements AutoClosable {
static {
System.load("/path/to/tree.so");
}
private long handle;
public Tree() {
this.handle = initialize();
}
#Override
protected void finalize() {
if (this.handle != 0) {
this.close();
}
}
#override
public synchronized void close() {
dispose(this.handle);
this.handle = 0;
}
private static native long initialize();
private static native void dispose();
}
However, the result is that I have some global objects floating around in memory that are only accessible via a C-style pointer. Is there a more modern way of achieving the same, i.e., using smart pointers or directly designing the C++ objects as members of the Java class?
I'm attempting to modify a C++ class in such a way that an Objective C object is one of its instance variables.
In essence, this question is like the reverse of this: C++ classes as instance variables of an Objective-C class
The header file MyCPPClass.h looks like this:
namespace my_namespace{
class MyCPPClass: public my_namespace::OperationHandler {
public:
void someFunc();
private:
void otherFunc();
};
}
And the MyCPPClass.mm file looks like this:
#include "MyCPPClass.h"
#import <Foundation/Foundation.h>
void my_namespace:: MyCPPClass:: someFunc() {
NSLog(#"I can run ObjC here! Yahoo!");
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"download_url_goes_here"]];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// handle the data
}];
}
void my_namespace:: MyCPPClass:: otherFunc() {
NSLog(#"I can run ObjC here! Yahoo!");
}
The issue here is that I need to store a reference to an object created in someFunc() and then use it in otherFunc(). This object is an instance of NSURLSessionTask. My background in C++ is very limited :(. What is the least intrusive way to do this?
I believe you can store it as a void*, according to this Pass an Objective-C object to a function as a void * pointer
So your header would become:
namespace my_namespace{
class MyCPPClass: public my_namespace::OperationHandler {
public:
void someFunc();
private:
void otherFunc();
void *mySessionTask;
};
}
If you want to send a message to it you first cast it back to what it is supposed to be:
[(NSURLSessionTask*)mySessionTask someMessageWithArg: foo]
There are two class A, B
m_Class, m_MethodID are not NULL..
But system will crash after call env->CallVoidMethod, Why?
public Class A extends Activity {
protected void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
B BClass = new B();
BClass.setFunction(this, "testFunc");
}
public void testFunc() {
Log.e("", "Test");
}
}
public Class B {
public native void setFunction(Object caller, String method);
}
-------------JNI-------------
JNIEXPORT void JNICALL Java_com_B_setFunction(JNIEnv *env, jobject thiz, jobject clsCaller, jstring sMethod) {
string sMethodName = jstring2str(env, sMethod);
jclass m_Class = env->GetObjectClass(clsCaller);
jmethodID m_MethodID = env->GetMethodID(m_Class, sMethodName.c_str(), "()V");
env->CallVoidMethod(m_Class, m_MethodID);
}
I had resolve this issue..
I refer to this page Android NDK: calling java method from JNI C class
and modify the code in JNI
from
env->CallVoidMethod(m_Class, m_MethodID);
to
env->CallVoidMethod(clsCaller, m_MethodID);
Because CallVoidMethod is trigger by instance(clsCaller) not class(m_Class)
For example, I have a Java class:
public class JniTest {
public int member_int_;
public int member_int_2_;
public boolean member_bool_;
public static int member_static_int_ = 90;
public String member_str_;
void print() {};
}
in JNI, I want to get each member without knowing their names in advance:
JNIEXPORT void JNICALL Java_com_tencent_qqmail_protocol_JniMethodTest_TestObject(JNIEnv * env, jclass cls, jobject obj) {
*for (each member in obj)* { ... } //how to implement ?
}
Starter : Trail: The Reflection API
Method getDeclaredMethods returns an array of Method objects, excluding inherited methods
Method getDeclaredFields returns an array of Field objects, excluding inherited fields
Method getSuperclass returns the super class
As #technomage commented, doing all the work in JNI is hard to do correctly (especially if you don't like memory leak and crash)
If you are not already "fluent" in JNI, start with simpler goals.
I have a peculiar issue with Android and a JNI library that I am using. The JNI library is named libBase.so and is properly included in the APK. I am able to do System.loadLibrary("Base") and the library gets loaded and can be used without any problems.
Consider the following code snippets:
/* NativeObject.java */
public class NativeObject
{
/* ... */
static
{
System.loadLibrary("Base");
}
}
/* ObjectUtil.java */
public class ObjectUtil
{
public static native NativeObject readNative(String path);
public static native void writeNative(String path, NativeObject obj);
}
When I attempt to do ObjectUtil.readNative() in the beginning of my program, I get the following issues in logcat:
05-19 18:57:38.508: WARN/dalvikvm(365): No implementation found for native Lcom/navimatics/base/ObjectUtil;.readNative (Ljava/lang/String;)Lcom/navimatics/base/NativeObject;
05-19 18:59:15.409: ERROR/AndroidRuntime(365): java.lang.UnsatisfiedLinkError: readNative
My understanding is that because the class ObjectUtil references NativeObject, loading the ObjectUtil class should force the loading of the NativeObject class and thus the execution of the NativeObject static initializer which performs the System.LoadLibrary() call. It is not clear then why I would get the aforementioned logcat messages.
However if I modify ObjectUtil.java to read as follows there is no exception and the program works:
/* ObjectUtil.java */
public class ObjectUtil
{
public static native NativeObject readNative(String path);
public static native void writeNative(String path, NativeObject obj);
static
{
System.loadLibrary("Base");
}
}
It also works if I do this:
/* ObjectUtil.java */
public class ObjectUtil extends NativeObject
{
public static native NativeObject readNative(String path);
public static native void writeNative(String path, NativeObject obj);
}
Any help on this would be greatly appreciated.
UPDATE:
The native side code that I was asked for is this:
static jobject JNICALL readNative(JNIEnv *env, jclass jcls, jstring jpath)
{
// ...
}
static void JNICALL writeNative(JNIEnv *env, jclass jcls, jstring jpath, jobject jobj)
{
// ...
}
static JNINativeMethod methods[] =
{
{ "readNative", "(Ljava/lang/String;)Lcom/navimatics/base/NativeObject;", readNative },
{ "writeNative", "(Ljava/lang/String;Lcom/navimatics/base/NativeObject;)V", writeNative },
};
The methods are registered using JNIEnv.RegisterNatives().
You have two extra parenthesis in initializing the JNINativeMethod array, this code shouldn't compile.
Check the return code of RegisterNatives(), it should be zero on success.
Also in case you're compiling a C++ code, you should declare the native methods extern "C" to avoid getting them mangled.