NDK for beginner : where/how to add source files? - android

I'm trying to use the NDK, and I've been working with official samples to get started. I've managed to get the "Hello JNI" sample working :
https://github.com/googlesamples/android-ndk/tree/master/hello-jni
(I work with Android Studio)
But I don't know how to add source files to the project. So, for example, the JNI function provided is (I removed macros that aren't useful for this) :
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
Let's say I want to get the string from a C function "getString()", defined as below:
const char* getString()
{
return "Hello from getString()";
}
//Then, JNI function becomes
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, getString());
}
If I create a getString.h where I declare the function, and put the implementation in my current and only one source file, everything works like a charm. So, headers do work.
However, If I create a GetString.c file where I put the implementation, my code stops compiling. I do understand it's because GetString.o doesn't get linked (or even compiled ? I don't know), but I have no idea about how to tell Android Studio to compile both hello-jni.c (sample's source fil) and GetString.c
Should I get my hands dirty and modify gradle files ?
I also heard about a file called Android.mk which acts like a makefile, but I haven't found it in the project. If it's mandatory, should I create it, and what to put in it ? If it's not, how does the IDE knows he must build hello-jni.c ? (AKA "how did the project get configured before I modified it")
If you need any information about java-side things or gradle scripts, just have a look on the github link :)

Well, a bit of testing was enough to confirm what I thought. I just had to create Android.mk :
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := hello-jni.c, GetString.cpp
LOCAL_MODULE := hello-jni
include $(BUILD_SHARED_LIBRARY)

Related

Android Studio JNI with C++ UnsatisfiedLinkError

I am using Android Studio 2.1.2 I am not using Experimental Plugin
Please Check the following files and Check the error I am getting.
I solved the issue. Edited file is as follows. They way I fixed it may not be the correct way as I am setting property to use deprecated way, but it works. Experimental plugin can still be unstable. I will try with experimental plugin soon.
build.gradle from Module
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
/*jni.srcDirs = [] not using this, I commented this. Please check SO links which explained when to use this and when not to use this*/
}
following 4 files are in jni folder of main
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib
LOCAL_SRC_FILES := HelloJni.cpp
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := all
HelloJni.cpp
#include <jni.h>
#include <Header.h>
JNIEXPORT jstring JNICALL Java_com_example_m1035325_ndksampleapp_MainActivity_getStringFromJni(JNIEnv *env,jobject thiz)
{
env-> NewStringUTF ( "Hellofrom JNI!");
}
Header.h
#include <jni.h>;
using namespace std;
#ifndef HEADER
#define HEADER
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_m1035325_ndksampleapp_MainActivity_getStringFromJni(JNIEnv *env, jobject obj);
}
#endif //NDKSAMPLEAPP_HEADER_H
MainActicity.java
static
{
System.loadLibrary("mylib");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvHello=(TextView)findViewById(R.id.tvHello);
tvHello.setText(getStringFromJni());
}
public native String getStringFromJni();
Here when I hover on method getStringFromJni it shows Can't resolve corresponding JNI function
I have set NDK path in Project Structure and in Path environment variable too.
I am getting following error
Process: com.example.m1035325.ndksampleapp, PID: 12831
java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.example.m1035325.ndksampleapp.MainActivity.getStringFromJni() (tried Java_com_example_m1035325_ndksampleapp_MainActivity_getStringFromJni and Java_com_example_m1035325_ndksampleapp_MainActivity_getStringFromJni__)
at com.example.m1035325.ndksampleapp.MainActivity.getStringFromJni(Native Method)
I searched a lot on SO also but I am not getting what I missed?
No error now , above error is fixed. Please check my answer to this question.
I think the problem is in your Android.mk file:
LOCAL_SOURCE_FILE := HelloJni.cpp
AFAIK the Android build system doesn't use variable of that name. It should be:
LOCAL_SRC_FILES := HelloJni.cpp
One important change I made is in file gradle.properties, is as follows
android.useDeprecatedNdk=true
so its related to Android Studio version 2.1.2 , for this version experimental plugin is the default option to use and the approach I am using is deprecated.
I will be trying with experimental plugin soon.

about mangled name of functions in c++

ı have closed source shared library that links against libmedia.so from android
but unforunatly in >android 5.0 ABI changes that made by google broke my closed source lib (thanx google)
now ı have the following error --> dlopen failed cannot locate symbol "_ZN7android11MediaPlayer13setDataSourceEPKcPKNS_11KeyedVectorINS_7String8ES4_EE" referenced by "mylib.so"
then ı hexedited my closed source library(mylib.so) to load myhack.so (that ı built myself) instead of libmedia.so (so ı simply made a trick)
here is the Android.mk of myhack.so library:
include $(CLEAR_VARS)
LOCAL_SRC_FILES := hack.cpp
LOCAL_SHARED_LIBRARIES := libmedia
LOCAL_MODULE := myhack
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
include $(BUILD_SHARED_LIBRARY)
and here is the hack.cpp:
extern "C" void _ZN7android11MediaPlayer13setDataSourceERKNS_2spINS_17IMediaHTTPServiceEEEPKcPKNS_11KeyedVectorINS_7String8ES9_EE();
extern "C" void _ZN7android11MediaPlayer13setDataSourceEPKcPKNS_11KeyedVectorINS_7String8ES4_EE() {
return _ZN7android11MediaPlayer13setDataSourceERKNS_2spINS_17IMediaHTTPServiceEEEPKcPKNS_11KeyedVectorINS_7String8ES9_EE();
}
let me explain a bit:
1-as I said I hexedited my closed source lib to load myhack.so instead of libmedia.so (source file and android.mk file of myhack.so are in above)
2-I linked myhack.so to libmedia.so(as you can see) to provide other libmedia functions via myhack.so
LOCAL_SHARED_LIBRARIES := libmedia
3-
lost symbol: _ZN7android11MediaPlayer13setDataSourceEPKcPKNS_11KeyedVectorINS_7String8ES4_EE
current symbol in libmedia.so: _ZN7android11MediaPlayer13setDataSourceERKNS_2spINS_17IMediaHTTPServiceEEEPKcPKNS_11KeyedVectorINS_7String8ES9_EE
so if lost symbol is called, it will return original function
my question is should I use extern "C" void or extern "C" int..... int or void? which one? setDataSource is a function and I dont think it is returning integer value so it should be void ı think but ım not %100 sure
could anyone help me please? thanx
Sooner or later you will get in troble with it. I remember problems with skia on project I work on. Some vendors were doing small changes with interfaces and that was causing various crashes.
To find return type you can investigate sources for android, steps are below:
You can use https://demangler.com/, to see signature of your function, it should be:
android::MediaPlayer::setDataSource(android::sp<android::IMediaHTTPService> const&, char const*, android::KeyedVector<android::String8, android::String8> const*)
now lets search android sources for such signature, you can find it here:
http://androidxref.com/6.0.1_r10/xref/frameworks/av/media/libmedia/mediaplayer.cpp#148
so its return type is status_t, which is typedef int status_t;, so as you have assumed its int.

Using opencv in native code for Android app development

I have namespace error building with ndk-build for native code in my Android app. The error sample is
C:/adt-bundle-windows-x86/ndk/sources/cxx-stl/gnu-libstdc++/4.6/include/bits
/allocator.h:54:1: error: unknown type name 'namespace'
C:/adt-bundle-windows-x86/ndk/sources/cxx-stl/gnu-libstdc++/4.6/include/bits
/allocator.h:55:1: error: expected ',' or ';' before '{' token
For OpenCV settings, my Application.mk file is
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi
APP_PLATFORM := android-10
That means I am using gnu-libstdc++ in compiling the native code.
My jni.c has c extension. That is I receive from my third party and they prefer in .c extension as if they have to change to .cpp extension, they have to change a lot in their other libraries.
So far is OK, I did all project settings for OpenCV for native development and if I do ndk-build, I can make .so libraries.
The problem of those namespace error happened when I include OpenCV's header file #include <opencv2/contrib/detection_based_tracker.hpp> into jni.c and I got a lot of name space error. If I include that #include <opencv2/contrib/detection_based_tracker.hpp> into cpp file, no error.
My questions are
(1)Is the error is because of using .hpp file in .c file?
(2)If I have no choice and have to use that .c file, is there way around to remove that error?
Thanks
My assumption would be that the file is compiled as a "C" file instead of a "C++" file because of the extension ".c". That means you cannot use any "C++" Code in your jni.c, wike classes or namespaces. These are obviously used however in your file "detection_based_tracker.hpp" that you are using.
So the problem is not that you include a file named ".hpp", but that this file contains "C++" code wich the "C" compiler cannot handle.
One solution to this problem is to only use the "C" functions in opencv (for example "opencv2/imgproc/imgproc_c.h" instead of "opencv2/imgproc/imgproc.hpp"). However, your function "detection_based_tracker.hpp" might not have a "C" version, as far as I can see.
The other option is to add a second file "function.cpp" with the "C++" functions that use opencv. The functions from "function.cpp" can be declared in a file "functions.h" and included in your "jni.c", so you can still use opencv c++ functions. Be careful to only use C style functions then, though (no classes, namespaces, ...) in your "function.h" file, otherwise you will have the same problems as before.

Wrapp existing library (.so) in jni for android app

I wanna realize this idea. I spent several days searching for information, but could not find anything. All tutorials say how to write my own library with JNI, but how to wrap already existing? I need just simple tutorial step by step (and why? if it possible). So I wanna start create native android application.
What I have :
I create C++ library in QTCreator by tutorial from youtube: simple library on C++ (.so) with headers (.h) which do simple cout in console:
Not compiled code mylib.cpp:
#include "mylib.h"
MyLib::MyLib() { }
void MyLib::Test() {
qDebug() << "Hello from our DLL";
// .so
}
Header mylib.h:
#ifndef MYLIB_H
#define MYLIB_H
#include "mylib_global.h"
#include <QDebug>
class MYLIBSHARED_EXPORT MyLib
{
public:
MyLib();
void Test();
};
#endif // MYLIB_H
and mylib_global.h (I think it does't matter)
So after build I have myLib.so.
And now I need wrap it in my android app. So I don't understand what I need to do for it.
I'm develop in Android Studio. And what I know:
Create in java package LibWrappClass with native method - something like "simplePrint()":
public native void simplePrint();
I need to create in /src/main folder "jni". Create Android.mk, myLibWrapper.h and myLibWrapper.cpp. But I don't understand what I need to write in Android.mk for connect my myLib.so to "myLibWrapper.h", and where should I put my library with headers. Can anyone help?
After adding the native method in your java code, You simply build the project. Now you need to move to the location where the class files are written by your IDE. Since you use Android Studio, it must be somewhere your project folder with path
out/production/YourModuleName
Open the location in commandline and run the javah command to generate the header file for your native function
javah -d <your jni folder path> <com.YourPackage.YourClass>
The class YourClass should be where you have declared the native method. This command will create a header file with name something like com_YourPackage_YourClass.h with a function declaration looks like
JNIEXPORT void JNICALL Java_com_YourPackage_YourClass_simplePrint
(JNIEnv *, jobject);
Implement this function in a C/C++ file with whatever operations you have to perform on jni side.
Then, Define the Android.mk file, In this case it will be something like
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lib
LOCAL_SRC_FILES := lib.c
include $(BUILD_SHARED_LIBRARY)
If you have multiple source files add them in LOCAL_SRC_FILES separated by space.
Next, go to your jni folder and build the project using command ndk-build. This will place the .so file inside the android folder libs/armeabi
Finally rebuild the Android project from Android studio and run.
For detailed instructions and complete source checkout this gist

Android std and stl support

I am playing with android ndk. I am using Window Vista with cygwin (latest version). I compiled and launched the hello world jni sample on my phone. It is working. The code is (is a .cpp file):
#include <string.h>
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_org_android_helloworld_HelloworldActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis);
};
jstring Java_org_android_helloworld_HelloworldActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis)
{
return env->NewStringUTF("Hello from native code!");
}
I wanted to add some modifications, just to play with it a bit:
#include <algorithm>
and then, in the function above, i added:
int a;
a=std::min<int>(10, 5);
but the compiler says that it cannot find the file 'algorithm' and that min() is not part of std.
After a bit of searching, i have found that the android ndk has a gnu-libstdc++ directory with all the std files needed. Reading the NDK docs, i have learned that usint std::* should work without any modification to the code (if one include the proper header files). But it seems that gcc on cygwin is not able to find the needed files.
What are the steps to do in order to be able to use std and stl within a .cpp file in an android ndk app?
From NDK r5's docs/CPLUSPLUS-SUPPORT.html:
By default, the headers and libraries for the minimal C++ runtime system
library (/system/lib/libstdc++.so) are used when building C++ sources.
You can however select a different implementation by setting the variable
APP_STL to something else in your Application.mk, for example:
APP_STL := stlport_static
To select the static STLport implementation provided with this NDK.
Value APP_STL values are the following:
system -> Use the default minimal C++ runtime library.
stlport_static -> Use STLport built as a static library.
stlport_shared -> Use STLport built as a shared library.
gnustl_static -> Use GNU libstdc++ as a static library.
Which NDK are you using? Have you tried compiling one of the sample applications that utilize the STL such as test-libstdc++?

Categories

Resources