Im getting the mentioned error while declaring static native method:
From the native side the link is working as expected:
Everything works as expected the problem is just the link(Android Studio 3.1.1) from java to the native reference which is broken for static(works for non-static ones) methods.
There exists a funny workaround:
remove the word 'static' in Java.
click on the red bulb to 'Create function Java_…_jniInitCore()'
restore the word 'static' in Java file.
Try to change the type of the second argument in JNI method from jclass to jobject. Worked for me
I am not expert but I changed the second parameter from jobject to jclass and it worked for me.
Related
I'm debugging an android application and am confusing about one android shared library and here i wanna explain the problem.
There is a native function defined in java code (java side) like this:
public static native void nativeInitialize();
Package:
package com.example.ExClass;
I loaded the shared library in IDA Pro.
I thought that i can find that function in exported functions and it should be something like this Java_com_example_ExClass_nativeInitialize
But the function is not in export list and as i debugged the java side codes, i know the function is calling from java but i don't know how it is working ?!
If the name-mangling scheme (Java_...) is not used, then it is possible the library uses the RegisterNatives approach to associate method names with function pointers. Try searching for structs that embed the string "nativeInitialize", they should be the methods argument to RegisterNatives. Look through the JNI_OnLoad function for the call site.
On Aarch64 assembly it should look like:
ldr x4, [x8,#1720] # RegisterNatives is the 215th member, times 8 bytes per pointer
bl x4 # Could also be a br if this was the last call of the function
I've successfully written a go mobile library and used it from an Android Java app.
I've also successfully called a c function from JNI. This one has the JNIEnv pointer.
I'd like to pass the context/JNIEnv from Java to Go.
Here's where it seems to get complicated:
Go Mobile does not seem to use JNIEnv at all.
LoadJNI.java does have a context object and claims to pass it to the Go side. But there it isn't visible.
I've been able to include jni.h using the "C" directive
I can transfer the JNIEnv from JNI/C or store it there as a global variable but that's an ugly kludge.
https://github.com/golang/go/issues/16876 talks about a proposal to do reverse binding but it's unclear what the status is.
https://godoc.org/golang.org/x/mobile/cmd/gobind also talks about it but the following code snippet
import "Java/java/lang/System"
t := System.CurrentTimeMillis()
returns
cannot find package "Java/java/lang/System" in any of:
/usr/local/go/src/Java/java/lang/System (from $GOROOT)
/Users/----/go/src/Java/java/lang/System (from $GOPATH)
In C I solved it with a compiler directive: #cgo CFLAGS: -I...
But I'm not sure how to do it for Java
In short, I'm now stuck and would appreciate help. Ideally, I'd just like to pass the object as a parameter, if possible. However, I'd settle for any reverse binding to call Java from Go.
I hate to answer my own question, but I'd hate someone to go through this as well too. I'll update this answer as I go along.
Basic Setup
Edit build.gradle and add GOPATH and GO to gobind (additions in bold):
gobind {
pkg = ".../reverse/reverse"
GOPATH = "/Users/johndoe/go"
GO = "/usr/local/go/bin"
}
How to develop
Start from the reverse example. Get it to compile and run.
Make one modification and recompile.
Missing gobind
It turns out the installation process neglected to build gobind. Run go install golang.org/x/mobile/cmd/gobind
I'm Stuck
Trying to modify the reverse project to do anything other than what it's already doing is failing. Even stuff like passing an object and calling toString will fail.
More to come...
I'm trying to make native (C++) cross-platform logging for desktop and Android. For that I made an abstract native Logger class, and the appropriate inherited classes (StdoutLogger, AndroidLogger, etc.) with the implemented log methods.
So since what Android supports for native logging is the __android_log_print(int prio, const char *tag, const char *fmt, ...) method which works with a printf-like syntax with indefinite number of arguments, I made the abstract log method to work with a similar syntax:
virtual void log(int aLogLevel, const char *tag, const char *format, ...)=0;
Well, for passing these indefinite numbers of arguments to the Android logging method I found I need to use another method which does the same but takes a va_list instead of the indefinite number of parameters. It also exists in log.h and is called __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap), so I just have to pass the arguments for that method instead.
The problem is that for all this to work I need stdarg.h which contains all the stuff I need for this (like the declaration for va_list, etc.), but by default Eclipse can't find it:
Unresolved inclusion:
In Eclipse project settings my include directories include:
${NDKROOT}/platforms/android-9/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
It's pretty strange that it's missing, because stdarg.h is part of the C standard library.
So I searched and found it here so I added it to the include directories:
${NDKROOT}/toolchains/arm-linux-androideabi-4.6/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.6/include
After this I got it working nicely, but I'm unsure if this is the correct way to do it.
Anyway, later (when implementing the log method in another inherited class) I ran into another problem regarding vfprintf method:
virtual void Log(int aLogLevel, const char *tag, const char *format, ...)
{
if (aLogLevel >= loglevel)
{
va_list args;
va_start (args, format);
vfprintf (stdout, format, args);
va_end (args);
}
}
Eclipse indicated an error:
Invalid arguments ' Candidates are: int vfprintf(__sFILE *, const char *, char *) '
I checked and apparently it wanted a __va_list (with __ at the beginning), not a va_list and it looked like they apparently weren't compatible (according to Eclipse). This vfprintf was in stdio.h, so I started searching and found another stdio.h in a similar folder I found stdarg.h, so I added it to include directories too (and moved it to top) :
${NDKROOT}/toolchains/arm-linux-androideabi-4.6/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.6/include-fixed
Now all errors are gone and everything seem to be working, but I'm really unsure if this was really the correct way to do it.
I have latest Eclipse/CDT/NDK/etc.
Any help would be appreciated.
Edit:
fadden: Thanks for the answer. This comment would have been too long so I put it here.
Yes, I use ${NDKROOT}/ndk-build.cmd to build the native part.
Yeah, I noticed cases when Eclipse would indicate errors so it wouldn't even let me start to build the app, but when I restart Eclipse without opening the file where the error was, it actually builds successfully. But when I open the file which it thinks has errors, it wouldn't let me start to build again. So it really looks like there's quite some inconsistency between errors indicated by Eclipse and actual errors (preventing building). Maybe I should just find a way to let it build regardless of indicated errors, though it could be annoying having errors in the project that should be just ignored, not knowing what are real and what are not so real errors... Or just add everything that is needed to include directories to keep Eclipse happy, so far it seems to be working, just wasn't sure if this was really the right way to do it. Thanks for the help.
Making Eclipse + CDT happy with Android is largely a matter of trial and error. (Days or perhaps weeks have been irrevocably lost trying to get size_t to work right.) Ultimately you should be building with the NDK toolchain, not Eclipse, so as long as Eclipse seems to be happy you're probably in good shape because ultimately it won't affect your binaries.
As you've noticed, some of the headers (like stdarg.h) are provided by gcc, not bionic, so it is necessary to dig around a bit. I expect there's a define somewhere that equates __va_list to va_list if you have the right #defines set (maybe __need___va_list??).
I have a library that I want to customize its work (I mean replacing the main of the library by my outside program so I can for example reorder the calls of the library's functions) and for that I had to change some static variable to extern variables so the library and my program can handle them both.
My program works fine when on run on my Linux desktop machine, however when I tried to port that to Android using NDK, I couldn't compile it because of the extern keyword .
So, I was wondering if there's a way concerning the Android.mk or alternative to the use of extern keyword, to make the things work.
You're getting an "undefined reference" because extern is not a variable definition, only a declaration. That means you must have an non-extern declation somewhere else in your code, otherwise the compiler doesn't allocate a symbol for your variable.
As for alternatives, no, an extern variable is the only way to have a global variable accross multiple object files in C.
If you only need a global variable in the same object file, you can use static so the symbol is not exported to other objects.
I'm just messing around with a Ndk tutorial I found. The native code uses one "package", while the activity is in another. When this mismatch occurs, I can't call the native function without getting an unsatisfied link exception. I know the "why's" I just don't know the resolution.
Here is the sample .c code that I've placed in my jni folder:
#include <string.h>
#include <jni.h>
jstring Java_com_mindtherobot_samples_ndkfoo_NdkFooActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) {
return (*env)->NewStringUTF(env, "Hello from native code!");
}
Notice that this .c code's package translates to com.mindtherobot.samples.ndkfoo.NdkFooActivity.
If I create a new activity that matches that Package/Class, I can call the invokeNativeFunction just fine. However, what if I can't match it? What if instead I need to run it from com.mydomain.activity?
I figured I could maybe change things around, such that my native declaration looked like this:
package com.mydomain;
public class Activity {
private native String com_mindtherobot_samples_ndkfoo_NdkFooActivity_invokeNativeFunction();
}
But that's a no-go. Just to be clear, I know how to make this work if I change my package to match what is compiled in the .c code; however, I need to be able to call a method from a different package... is this possible?
You need to make a basic class with the sole functionality of talking to C, not an activity. Then activities can instantiate this class, or possibly even statically call it, whenever they need to talk to C.
Your question is pretty scrambled, but the package declaration in the Java source code has to agree with what is encoded into the native method name, i.e. it must agree with what is generated by javah. If you change the package in the Java code, you must regenerate the .h file, and adjust the .c file to suit. There is no other way to fudge around this.