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...
Related
I am setting up a Bitrise on an Android app. I have everything set that is related to the flow, the thing is that I have a custom python builder which requires a parameter. My build-flow is triggered through API, which is working as expected - the only thing missing is passing that parameter to the build script.
What I have currently:
I have added an env-variable; named PARAM
My JSON body, that triggers the build
{
"hook_info":{
"type":"bitrise"
},
"build_params":{
"branch":"master",
"workflow_id":"test_args_script",
"commit_message":"Triggered from postman",
"environments":[
{
"mapped_to":"PARAM",
"value":"123456",
"is_expand":true
}
]
}
}
On the Bitrise flow I have a script step which is actually calling the python script as follows: python builder.py $PARAM
EXPECTATIONS:
My expectations are that the value that will be passed to the script will be the same as the one in the API payload (123456).
If anyone has recently worked on something similar, or if any of you guys can point me out to documentation that will help me to achieve the above I will be really thankful!
I actually found the way. The problem on my end was that I had an env-variable already named the same as the one variable that has been passed from the API. After changing the name to a different one, I was able to get the parameter and pass it to the build script. If anyone needs more info, give me a shout and I can post the payload along with the flow settings.
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'm trying to do the impossible and it doesn't seem to be working. The overall goal is this: I have a Component interface and I would like to show the user a list of classes on the classpath that implement that interface. The trick is, it has to run in Android.
Near as I can tell this is impossible to do at run time. The java mechanism (ServiceLoader) has been intentionally crippled by the Android toolchain, so it doesn't work. Guava doesn't work on Android, nor does ClassUtils, nor does Reflections.
At this point I've been yak shaving for 8 hours strait and there's no end in sight, so I'm looking for alternative approaches. My current thinking is to build a plugin (very much like sbt-spi, but not, because Android hates SPI) that can generate a text file at compile time that lists every class which implements the interface, so that at runtime I can open that file as a resource and then use reflection to start building them. Is that a reasonable idea? How should I go about it? (my current approach is "read the sbt-spi plugin source and try to copy it", but this seems like a scenario where "ask for wisdom" is a better approach)
Got it! I ended up using sbt-spi after all (huzzah not reinventing any wheels!) and just moving the output into the intermediate assets directory as part of the resourceGenerators task:
lazy val androidEntryPoint = (project in file("android-entry-point"))
.dependsOn(core, components, androidComponents)
.enablePlugins(SpiPlugin)
.settings(commonSettings: _*)
.settings(resourceGenerators in Compile += Def.task{
// This task copies the list of Components to the appropriate place to ensure
// it gets included in an accessible place in the APK
val res = collectResources.value._1 // item _1 here is for assets, item _2 is for resources. See the output of sbt "show androidEntryPoint/android:collectResources"
mapExport.value.toSeq.map { name =>
IO.move(target.value / name, res / name)
res / name
}
}.taskValue
)
That said, I'd love to hear a better approach if you can think of one. If none turn up in the next week or so I'll mark this one the answer.
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 am trying to create unit tests for a REST client that does some API calls. The client works fine in the live application, but I can't get it to run in the test case.
Apparantly, LoaderTestCase.getLoaderResultSynchronously() could be used here (at least according to Android reference, but it will not accept my loader. The code:
public void testGetEventInfo() {
// Init some vars
...
// Create & execute loader
RESTLoader loader = new RESTLoader(getContext(),
RESTLoader.HTTPVerb.GET, action, params, LOADER_GET_NEWS);
getLoaderResultSynchronously(loader);
}
This yields the error getLoaderResultSynchronously(Loader) in the type LoaderTestCase is not applicable for the arguments (RESTLoader).
RESTLoader extends AsyncLoader. Note that I'm using the supportlibrary, maybe the Loader in there is incompatible? The documentation gives no information on this.
I've tried to solve this in several ways, though none seem to work:
Registered a listener to loader. However, the callback never triggers
Using CountdownLatch (also with a listener). Again, no trigger/countdown timeout.
Playing around with the type template (), without success.
Similar solutions on SO, though again failing to reach the listener.
Does anybody know why getLoaderResultSynchronously will not accept the loader? Or another clean way of testing the Loader, including a way to test return data? I can test handling the return data in a separate case, but I would also like to test the actual data.
Sincerely,
Have you taken a look at the source code? You'll find the following import statements:
import android.content.Loader;
import android.content.Loader.OnLoadCompleteListener;
It doesn't look like Android offers a support version for LoaderTestCase. The easiest solution would be to temporarily change to the non-support LoaderManager (that is, have your class make use of the android.content.Loader instead), test your app, and then switch back to the support implementation. You might also consider copying the testing source code into your project, import the support LoaderManager, and execute it directly. I'm not familiar with the test libraries for Loaders but it doesn't seem outwardly obvious that this would cause any major issues.
You can get sources from LoaderTestCase here, create SupportLoaderTestCase class from that sources in your test project and modify all namespaces to support library namespaces (e.g. change android.content.Loader with android.support.v4.content.Loader). Than you can extend your test case from SupportLoaderTestCase (not from LoaderTestCase) and use it without problems
The method you are trying to call (getLoaderResultSynchronously) accepts an object of type android.content.Loader. If your RESTLoader class is not of that EXACT type then you will get this error. I suspect your class directly or indirectly extends android.support.v4.content.Loader, which would explain the error.
I am not aware of a back-port of LoaderTestCase that would support testing of this type of class.