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.
Related
i am making an Android Shapefile reader app on Android with the use of NDK. I want to use C++ to parse shape files. I found library "Shapefile C Library". Github: https://github.com/sw897/shapelib.
I am using Android studio and have no idea how to import it to my Android project so i could use functions described in: http://shapelib.maptools.org/shp_api.html
Any tips?
First, start hacking away at the Hello-JNI example from Google:
https://github.com/googlesamples/android-ndk/tree/master/hello-jni
Use that as a test bed.
Then, create a Java Class with public native methods that let you interact with your library. Something of a high level API, probably to pass a file name or buffer to SHPOpenLL and return a pointer to the ShapeFile context (ShapeHandle). Looks like your library is written in C, so you should be able to write a similar interface to query the shapefile passing a (jint) c-style cast of your ShapeHandle pointer.
Next, you need to play around with javah to generate the header for your shapefile interface. Once the header is generated, you can use it to implement your interface in a .cc file. From there you will basically make Java calls to your C interface and it will return jobjects. (jint, jbool, jstring, etc...)
I'm looking at the ShapeLib that you want to use and while it's easy enough, there will be some gotchas.
You will need to implement SAHooks for file I/O. I suggest looking at NVFile for an example how to access APK files (or downloaded files). You will need to init it using activity.context.assetmanager. Then use nv_file to wrap FRead/FSeek/etc...
Passing info back to java is going to be tough. Personally, I would build a Java class to hold the shape information and fill it out on the C side. However, you might be tempted to query these parameters from the SHPObject one at a time. Will be slow, but less error prone.
E.g.
// Java
MyJavaShapeObject obj = new MyJavaShapeObject();
_c_retrieveShapeObj((jint)pShapeFile, obj);
// C
java_blah_blah_c_retrieveShapeObj(JNIEnv* env, jclass activity, jint theShapeFile, jobject theObj){
ShapeHandle pShapeFileHandle = (ShapeHandle)theShapeFile; // c style conversion
// http://stackoverflow.com/questions/11647646/how-to-use-the-jni-to-change-the-fields-of-a-java-class
// http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp16613
// http://stackoverflow.com/a/36759159/7949696
}
I have managed to get SQLite setup using the NDK, but I can't manage to get custom functions to work which was the whole reason for implementing SQLite using the NDK.
I used this library to get the same SQLite files. It also contains an extension file called extensionfunctions.c which adds in string and mathematical functions for SQLite.
From what I can see, the SQLite implementation appears to be working correctly, but I cannot call any of the custom functions.
I've little to no knowledge of C/C++, so any help would be great. Do I have to compile the extensionfunctions.c file independently, and then add in the SO file with the libsqliteX.so file? Or do I have to make a call in the android_database_SQLiteCommon.cpp to load in the other extension? I've no idea how this works.
Edit
The file extensionfunctions.c is included in sqlite3secure.c which is in the Android.mk file under LOCAL_SRC_FILES. I assume that means the file is being used correctly, but none of the custom functions are accessible.
Edit 2
// To enable the extension functions define SQLITE_ENABLE_EXTFUNC on compiling this module
#ifdef SQLITE_ENABLE_EXTFUNC
#define sqlite3_open sqlite3_open_internal
#define sqlite3_open16 sqlite3_open16_internal
#define sqlite3_open_v2 sqlite3_open_v2_internal
#endif
#include "sqlite3.c"
#ifdef SQLITE_ENABLE_EXTFUNC
#undef sqlite3_open
#undef sqlite3_open16
#undef sqlite3_open_v2
#endif
I found the above code with the comment and I added in the line below but it appears as though nothing has changed.
#define SQLITE_ENABLE_EXTFUNC
Do I have to do anything to get the app to refresh it's version of the sqlite3 files or could that be a problem? My C skills are poor so I assume what I did is what the comment has referring to?
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 doing some cross platform development, and I ran across another weird problem..
namespace Math
{
#include <math.h>
}
This is what I am trying to do. It works fine on iOS, but on Android everything inside math.h is not a member of Math when I try to compile it. After some trial an error I found out that the ndk doesn't put the stuff in math.h in the namespace Math, as the functions do exist without the Math:: prefix. The big problem is that some functions in math.h clash with my own convenience functions. How can I fix this?
You really shouldn't try to put any system headers into a different namespace. The solution, as painful as it may be at this point in development, is to put all of your code into its own namespace.
What about <cmath>? it puts everything in std:: (in C++11).
Don't put your convenience functions in the global namespace.
Don't try to wrap math.h in a namespace.
There, all’OK now.
Cheers & hth.,
Probably something else includes math.h earlier in the preprocessor "pipeline". C includes check whether the sane include has been included earlier. In this case, the check #ifndef MATH_H resulted in excluding the whole file. Try placing your include with the namespace at the beginning of your .c files.
I compiled this library using NDK into an .so. I can successfully load it with System.LoadLibary() but when I try the example code for the library, it throws an UnsatisfiedLinkError. I checked the source code for the library and it indeed has the functions I'm using with the same parameters (except JNIEnv and jobject) along with the same returns. Plus, surely the creator of the library would share working example code. So what am I doing wrong? I copied the exact code from the page, and changed the variable names accordingly.
Without more detail it's difficult to say. Having looked at the library on that site, it's possible that you've named your class differently. You need to call your Java class Giffle and it has to be in the package org.jiggawatt.giffle. This isn't a naming convention that you would stumble upon by accident ;-)
package org.jiggawatt.giffle;
public class Giffle {
static {
System.loadLibrary("gifflen");
}
public native int Init(String gifName, int w, int h,
int numColors, int quality,
int frameDelay);
public native void Close();
public native int AddFrame(int[] inArray);
}
To use it, you would either make the calls in the Giffle class, or probably a cleaner way is to use an instance:
Giffle giffle = new Giffle();
giffle.Init(...);
giffle.AddFrame(...);
giffle.Close();
Does seem a bit odd that the Java part wasn't in the zip, especially as the class name is hardcoded into the C symbol. Maybe the guy who wrote it had a whole bunch of extra code in the Java part that he didn't want anyone to see.
As far as I can tell, the C and C++ code is complete. You shouldn't need to modify it at all. The implementations of the native methods are in gifflen.cpp, and have names like Java_org_jiggawatt_giffle_Giffle_Init. They have the correct native-side arguments for the JNI calling convention.
How are you building this? I found that I had to do Project->Clean… in Eclipse every time after running ndk-build to let Eclipse pick up the .so file and copy it to the apk.
Try this method. http://mpigulski.blogspot.com/2010/09/debugging-dlopen-unsatisfiedlinkerror.html it helps me.