Cannot use CallVoidMethod from another object - java-native-interface

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)

Related

How to retrieve C++ class member data from kotlin side?

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;
}
}

Connect life time of C++ objects to Java object

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?

How to Mock the return object

I try to mock a User class and its nested struct UserBuilder:
class User
{
public:
virtual int loadData(const std::string& filename);
virtual UserBuilder getUserBuilder(const std::string& functionName) const;
struct UserBuilder
{
UserBuilder(std::string functionName) : m_functionName{functionName} {};
virtual ~UserBuilder();
virtual UserBuilder& fun1();
virtual UserBuilder& fun2(int32_t num);
virtual bool callFunction();
private:
std::string m_functionName{};
};
}
This is the mock class for User:
class UserMock : public User
{
public:
MOCK_METHOD1(loadData, int(const std::string& filename));
MOCK_CONST_METHOD1(getUserBuilder, UserBuilder(const std::string& functionName));
};
Thsi is the mock class for UserBuilder:
struct UserBuilderMock : public User::UserBuilder
{
public:
UserBuilderMock(std::string functionName) : User::UserBuilder(functionName) {}
MOCK_METHOD0(fun1, UserBuilder&());
MOCK_METHOD1(fun2, UserBuilder&(int32_t num));
MOCK_METHOD0(callFunction, bool());
};
I want to test this function:
void useCase(std::unique_ptr<User> userP)
{
int status = userP->loadFile("init");
if (status == 0)
{
User::UserBuilder builder = userP->getUserlBuilder("init");
bool result = builder.fun1().fun2(1).callFunction();
return result;
}
else
{
return false;
}
}
I give the getUserBuilder("init") a mock object builderMock as its return value, like this:
auto userMock = std::make_unique<UserMock>();
ON_CALL(*userMock, loadFile("init")).WillByDefault(Return(0));
UserBuilderMock builderMock("init");
EXPECT_CALL(*userMock, getUserBuilder("init")).WillOnce(ReturnPointee(&builderMock));
EXPECT_CALL(builderMock,fun1()).Times(1);
The test log fail: fun1 never called-unsatisfied and active.
I want to use the builderMock object to call the mock method fun1,fun2 and callFunction, but it still use the real UserBuilder object
call the real fun1,fun2 and callFunction. What should I do to make it use Mock object call the Mock method?
You have to rewrite your code to make User::getUserBuilder return a pointer (possibly smart one) to UserBuilder.
With the method returning UserBuilder object
EXPECT_CALL(*userMock, getUserBuilder("init")).WillOnce(ReturnPointee(&builderMock));
getUserBuilder casts the mock to an object of its base class (slicing), losing all the the mock additions.

How to traverse all the members inside a jobject

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.

Android and UnsatisfiedLinkError

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.