I want to incorporate small, lean and mean C-based parser into my Android project. I've done JNI programming in the past but not any type of native (C) development on Android. My plan is to compile C lib into SO and create JNI wrapper around it which I'm going to use in my app. Is this how it can/should be done? Second and most important question - how can I include .so into my APK? Where would it go?
Depending on how much data you pass and how often I seriously doubt a Java/JNI/C would perform faster than a native java implementation.
Passing anything other than a "Java Int" to a "C long" invokes the JNI data conversion routines which are anything but lean and mean.
So unless your JNI routine fits the pattern:
Pass small amount of data.
Do lots and lots of work in C.
Pass small result set back.
You will be considerably slower than a native java implementation. If you stick with the basic "C" like java operations (+,-,*,==,>) using the "native" data types (int, char, String) and avoid the fancy libraries Java will perform nearly as fast as C.
The remaining bug-bear of java performance is the time taken to fire up the JVM and get everything going, but as you are starting off from a Java program this is a non issue.
The other reason for "slow" java performance is people insist on unnecessary factories, containers, xml beans etc. etc. where plain, simple methods would do the job better.
Create a JNI folder in your Android root Application folder(where there is src, res) .Place the code (1) there name it as someshared-lib.c.
(1)
Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jobject thiz)
{
//your c code , the JNI will act as a wrapper for it
return (*env)->NewStringUTF(env, "<string to pass or you can mention jchar * type string >");
}
(2)IN java file
package YourPackageName;
public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{
public native String NativeFuntionName();
String returnValue = NativeFuntionName();
}
(3)IN Android.mk do this :
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := someshared-lib //note the libname same as c file name
LOCAL_SRC_FILES := someshared-lib.c //this is the file where you placed the code (1)
include $(BUILD_SHARED_LIBRARY)
export your ndk-build
(do export PATH=:$PATH
go to the JNI folder as created above :
execute ndk-build command
you will get a library formed someshared-lib in the lib folder formed in the Application root folder.While building and running the application this will get bundled up with the apk and will get installed in the device.To verify this you can go to the
/data/data/your_package_name/lib folder.
The app searched this lib in the /data/data/your_package_name/lib ( also /system/lib as well ) folder and use it for the dynamic calls(JNI) being made from the Android application.
Now if you want to return anything other than string you have to change the above method declration in c file as below :
Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jclass obj,jobject thiz)
{
jfieldID fid;
jboolean enable_flag;
//Pass the class object having all the variable from the android app to the JNI in the jclass obj and access its members using the field ID and using Get and Set firld ID.
clazz = (*env)->GetObjectClass(env, info);
fid = (*env)->GetFieldID(env,clazz,"some_variable","X"); //"X" - type of variable for boolean it //is Z , for INtezer it is I, for double it is D,
refer this document for detailed explaination
//for getting teh value fomr the JNI
enable_flag = (*env)->GetBooleanField(env, **thiz**, fid);
//for setting the value
fid = (*env)->GetFieldID(env,clazz,"other_float_variable","D");
(*env)->SetDoubleField(env,**thiz**,fid,other_float_variable);
}
Also in the Android Application you have to pass the Class object of the structure.
e.g
(2) will become now :
package YourPackageName;
public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{
public native String NativeFuntionName();
String returnValue = NativeFuntionName( exampleStruct exst);
where exampleStruct :
public class exampleStruct {
protected boolean some_variable = 0;//no log saving by default
protected float other_float_variable = 0;
}
}
Hope this helps.
use Android NDK
Download n docs Android NDK 1.6
This will save you from writing JNI layer for lib and also will install the app in the lib folder of your app data folder.
Someone said that JNI in android sucks : http://www.koushikdutta.com/2009/01/jni-in-android-and-foreword-of-why-jni.html
Related
Is there any way to use jniRegisterNativeMethods to map JNI functions in a NDK app? i.e. use a method_table to map native (C/C++) functions via the JNI instead of using ridiculously long JNI method names?
For example, in one exercise I saw, there was a C file added onto the platform it self,
#include "core_jni_helpers.h"
#include "jni.h"
static jlong init_native(JNIEnv *env, jobject clazz)
{
return 0;
}
// ...
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
// ...
};
int register_android_server_ExampleService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/OpersysService",
method_table, NELEM(method_table));
};
But then the register_android_server_ExampleService was was manually invoked in services/core/jni/onload.cpp (on the platform)
Is there any way to do this or something similar with the NDK though?
My guess is no, as JNIHelp.h and core_jni_helpers.h aren't available in the NDK, and the Kotlin tools in Android Studio likely wouldn't be able to run a function in order to perform the auto complete. However I thought it was worth asking in the small case I could somehow avoid naming functions like Java_vendor_<name>_<name>_<name>_test_MainActivity_stringFromJNI
UPDATE # 4
I've successfully run the firstchar example, but now the problem is with using regex. Even after including header file, it is not recognizing regex operator. Any clue how can this be resolved?
UPDATE # 2
I've compiled sqlite3 library in my project. I am now looking for anyone to help me with writing a function for my regex, attach it to database and call it from query.
UPDATE # 3
I've written some code from this example. Here it is
extern "C"
void
Java_com_kfmwa916_testapp_DatabaseHandler_createFunction() {
sqlite3 *db;
//Open database
sqlite3_open("MyDBName.db", &db);
//Attach function to database
sqlite3_create_function(db, "firstchar", 1, SQLITE_UTF8, NULL, &firstchar, NULL, NULL);
}
And firstchar function is,
static void firstchar(sqlite3_context *context, int argc, sqlite3_value **argv) {
if (argc == 1) {
char *text = (char *) sqlite3_value_text(argv[0]);
if (text && text[0]) {
char result[2];
result[0] = text[0]; result[1] = '\0';
sqlite3_result_text(context, result, -1, SQLITE_TRANSIENT);
return;
}
}
sqlite3_result_null(context);
}
And the used it in my query like
SELECT firstchar(text) FROM dummy
But it is giving error
no such function firstchar()
Any help is highly appreciated.
Original Question
I am working with Arabic Language saved in UNICODE Format in SQLite. I want to implement a search. But there's a problem.
Let's say the text is
<html>
<head>
<style>
#font-face {
font-family: "Al_Mushaf";
src: url('fonts/al_mushaf.ttf');
}
#font-face {
font-family: "Jameel Noori Nastaleeq";
src: url('fonts/jameel_noori.ttf');
}
</style>
</head>
<body>
<h3 style='font-family:"Al_Mushaf"'>
صحابہ کرام کا انبیائے کرام کی سنّت پر عمل
میٹھے میٹھے اسلامی بھائیو!صدائے مدینہ لگانا انبیائے کِرام عَلَیْہِمُ السَّلَام کی اس قَدْر پیاری سنّت ہے کہ صحابۂ کِرام عَلَیْہِمُ الرِّضْوَان نے بھی اسے خُوب اپنایا اور وہ بھی حضرت سَیِّدُنا داؤد عَلَیْہِ السَّلَام کی طرح اپنے گھر والوں کو جگایا کرتے جیسا کہ حضرت سَیِّدُنا عبد اللہ بن عُمَر رَضِیَاللّٰہُ تَعَالٰی عَنْہُما فرماتے ہیں کہ میرے والِدِ مُحْتَرَم اَمِیرُ الْمُوْمِنِین حضرت سَیِّدُنا عُمَر فَارُوقِ اَعْظَم رَضِیَاللّٰہُ تَعَالٰی عَنْہ رات میں جس قَدْر ربّ تعالیٰ چاہتا،نَماز پڑھتے رہتے،یہاں تک کہ جب رات کا آخری وَقْت ہوتا تو اپنے گھر والوں کو بھی نَماز کے لیے جگا دیتے اور ان سے فرماتے: اَلصَّلٰوة یعنی نماز۔ پھر یہ آیت مُبارَکہ تِلاوَت فرماتے:
وَاۡمُرْ اَہۡلَکَ بِالصَّلٰوۃِ وَ اصْطَبِرْ عَلَیۡہَا ؕ لَا نَسْـَٔلُکَ رِزْقًا ؕ نَحْنُ نَرْزُقُکَ ؕ وَالْعٰقِبَۃُ لِلتَّقْوٰی (پ۱۶،طٰهٰ:۱۳۲)
</h3>
</body>
</html>
And it is stored in SQLite Database. Now I want to search html, it will return the result and if I search مبارکہ it won't return a result because in actual text, it is مُبارَکہ (with these extra UNICODE).
I want to ignore all HTML tags and these extra UNICODE Characters while searching so that html shouldn't return a result while مبارکہ should return a result.
What I found so far;
Make extra column and put stripped text into it and then search (I can't do it because there are thousands of books and they will take a lot of memory)
UDF Like SQL (I couldn't find any suitable example/tutorial to implement it)
Using REGEXP (I couldn't figure it out yet how to do this, I just know that I've to implement it myself)
SQL query using LIKE and GLOB operators and wildcard characters.
I'm stuck for two days and couldn't find a working solution. Option#4 is desirable but any working solution will do the charm.
Meanwhile, I've to keep application memory efficient and optimized searching.
Any help is highly appreciated.
UPDATE
I've made regex to ignore html tags and text between style tag and used it in query with REGEXP.
Now there are two problems,
I want to ignore these extra characters too. I know their UNICODEs, just need to know how to append it in the regex. This is my regex;
(?![^<]*>)(?!<style[^>]*?>)(TEXT)(?![^<]*?<\/style>)
I've used it in query like
SELECT text FROM dummy WHERE text REGEXP <myregex>
It's not giving an error but not returning the desired result too.
Answer to Original Question
NOTE: As I have recently learned it, I maybe wrong at many places, kindly correct my mistakes
There are two solutions
Use REGEXP Operator with SQLite Query
Implement your own User Defined Function using NDK
The problem with first one is that it returns either true or false but I need data. And the problem with both methods is that you have to use C/C++ Library in your Android Project. So I decided to create my own user defined function.
You can find many tutorials on how to use NDK in your project, but won't find any complete example of using 3rd Party libraries in your project.
After a lot of searching/studying, I combined things from many different places and was able to complete my task. Below are some steps on how you can do it. I also intend to write a complete step-by-step tutorial.
Getting things ready
First you need libraries which you want to use in your project. In my case, I need sqlite3 amalgamated library, which can be downloaded from here. Extract them in cpp folder of your project.
You might have familiar with CMakeLists.txt file by now when you included NDK in your project. It's time to add these libraries in CMakeLists.txt file. For that, go to your Project pane, you'll see External Build Files there and inside it you'll see the desired file. Open it and edit it as follows,
# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/native-lib.cpp )
include_directories(${CMAKE_SOURCE_DIR}/src)
add_library(sqlite3 STATIC src/main/cpp/sqlite3.c src/main/cpp/sqlite3.h src/main/cpp/sqlite3ext.h)
add_executable(sqlite src/main/cpp/sqlite3.c src/main/cpp/sqlite3.h src/main/cpp/sqlite3ext.h)
set_target_properties(sqlite PROPERTIES OUTPUT_NAME sqlite3)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
sqlite3
log )
You have to first add libraries using add_library and then link it to your class which you've made, it is by default named native-lib.cpp.
Build your project and you are ready to go.
Implementing Function
Now here comes the main part. Open native-lib.cpp and include required files and headers. What you have to do;
Make a function which you will be calling from YourActivity.java. You'll know the pattern once you see your file. In my case it is Java_com_kfmwa916_testapp_SearchResult_createFunction(JNIEnv * env, jobject object, jstring search) where
Java is a keyword
com_kfmwa916_testapp is your package
SearchResult is your Java class
createFunction is the name of the function.
Create your function. In my case, I have to apply certain regex in search. Here is mine,
static void strip_text(sqlite3_context *context, int argc, sqlite3_value **argv) {
if(argc == 1) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "inside strip_text");
char *result = (char *) sqlite3_value_text(argv[0]);
std::string text(result);
std::regex regex_head("YOUR REGEX");
if (!text.empty()) {
text = std::regex_replace(text, regex_head, "");
sqlite3_result_text(context, text.c_str(), -1, SQLITE_TRANSIENT);
__android_log_print(ANDROID_LOG_VERBOSE, "STRIPPED TEXT", "%s", text.c_str());
return;
}
}
sqlite3_result_null(context);
}
Create sqlite3 instance, open database, attach this function to database and use it in your query. Here is a code snippet
extern "C"
void
Java_com_kfmwa916_testapp_SearchResult_createFunction(JNIEnv * env, jobject object, jstring search) {
const char * search_term = env->GetStringUTFChars(search, 0);
env->ReleaseStringUTFChars(search, search_term);
std::string q(search_term);
std::string query = "SELECT text FROM dummy WHERE LIKE('%" + q + "%', strip_text(text))=1";
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "%s", query.c_str());
//GetJStringContent(env, search, search_term);
sqlite3 *db;
//Open database
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Opening database");
int rc = sqlite3_open("/data/data/com.kfmwa916.testapp/databases/MyDBName.db", &db);
//It'll be good to check 'rc' for error(s).
//Attach function to database
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Attaching function");
rc = sqlite3_create_function(db, "strip_text", 1, SQLITE_ANY, NULL, &strip_text, NULL, NULL);
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Executing query");
rc = sqlite3_exec(db, query.c_str(), callback, NULL, NULL);
}
Implement callback function to process result. It should look like
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "FOUND");
int i;
for (i = 0; i < argc; ++i) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "%s = %sn", azColName[i], argv[i] ? argv[i] : "NULL");
}
return 0;
}
Finally come to your Java class, in my case, it is SearchResult Load library and define function.
static {
System.loadLibrary("native-lib");
}
public native void createFunction(String search);
and call it where you want it. Let's say onClickEvent of a button like createFunction(searchterm)
Post is open for correction and modification.
I'm having a confusing problem. I'm trying to make a Web cleint that uses WSDL.
I'm using C++ RAD Studio 10 Seattle, but the same problem occured in RAD Studio XE8(older version).
1.I create a Multi-Device Application, add one Edit component and one Button.
2.I create a WSDL Importer by changing the location of the WSDL file to : "http://www.w3schools.com/webservices/tempconvert.asmx?WSDL" and leave all other setting to default.
3.On ButtonClick event of the button I write two lines of code :
_di_TempConvertSoap Converter = GetTempConvertSoap(true,
"http://www.w3schools.com/webservices/tempconvert.asmx?WSDL");
Edit1->Text = Converter->CelsiusToFahrenheit("32");
So after these three steps I have one unit, which is the main Unit with the Form and with the button event. And one file "tempconvert.cpp" that the WSDL Importer has generated. It quite actually just translates the WSDL code to a C++ one and defines the method to communicate with the server. In my case I have two methods : FahrenheitToCelsius() and CelsiusToFahrenheit(), in the example I use CelsiusToFahrenheit().
I compile it to 32-bit Windows platform, run it and when I click the button, the result "89.6" appears in the text of the Edit component. So this is working as expected.
But when I change the target platform to "Android" and use my mobile phone "Samsung GT-I8262" with Android 4.1.2 and run the project, it just stops and exits. I debugged the problem and it stops at the first command in "tempconvert.cpp" in RegTypes() method.
// ************************************************************************
//
// This routine registers the interfaces and types exposed by the WebService.
// ************************************************************************ //
static void RegTypes()
{
/* TempConvertSoap */
InvRegistry()->RegisterInterface(__delphirtti(TempConvertSoap), L"http://www.w3schools.com/webservices/", L"utf-8");
InvRegistry()->RegisterDefaultSOAPAction(__delphirtti(TempConvertSoap), L"http://www.w3schools.com/webservices/%operationName%");
InvRegistry()->RegisterInvokeOptions(__delphirtti(TempConvertSoap), ioDocument);
/* TempConvertSoap.FahrenheitToCelsius */
InvRegistry()->RegisterMethodInfo(__delphirtti(TempConvertSoap), "FahrenheitToCelsius", "",
"[ReturnName='FahrenheitToCelsiusResult']", IS_OPTN);
/* TempConvertSoap.CelsiusToFahrenheit */
InvRegistry()->RegisterMethodInfo(__delphirtti(TempConvertSoap), "CelsiusToFahrenheit", "",
"[ReturnName='CelsiusToFahrenheitResult']", IS_OPTN);
/* TempConvertHttpPost */
InvRegistry()->RegisterInterface(__delphirtti(TempConvertHttpPost), L"http://www.w3schools.com/webservices/", L"utf-8");
InvRegistry()->RegisterDefaultSOAPAction(__delphirtti(TempConvertHttpPost), L"");
}
#pragma startup RegTypes 32
Does someone have any idea why this might be happening? I tried on two other Samsung phones and it didn't work. The error that shuts the program down is "Segmentation fault(11)", and more precisely it stops at the following line of code in "System.pas" file :
u_strFromUTF8(PUChar(Dest), MaxDestChars, DestLen, MarshaledAString(Source), SourceBytes, ErrorConv);
Here is some info that I've found about the function:
u_strFromUTF8 - function that converts a UTF-8 string to UTF-16.
UCHAR is a Byte(in Delphi), so PUCHAR is a pointer to Byte.
I cannot se what could possibly go wrong with this function which apparently only converts a string.
So my question is why does the project work on Windows 32 bit version, but on Android it throws Segmentation fault(11)?
I hope I could find a solution for this problem. I will keep looking.
Thank you,
Zdravko Donev :)
UPDATE:
I disassembled the line:
InvRegistry()->RegisterInterface(__delphirtti(TempConvertSoap), L"http://www.w3schools.com/webservices/", L"utf-16");
to get :
TInvokableClassRegistry *Class = InvRegistry();
TTypeInfo *Info = __delphirtti(TempConvertSoap);
UnicodeString Namespace = "http://www.w3schools.com/webservices/";
UnicodeString WSDLEncoding = "utf-8";
Class->RegisterInterface(Info, Namespace, WSDLEncoding);
And I saw that the problem occurs when calling InvRegistry() function, but I still haven't found the problem as I cannot reach the source code of the function.
I found a solution.
I deleted the line
#pragma startup RegTypes 32
and called the method RegTypes() on my own when I create the form and it worked.
I have spent a ridiculous amount of time trying to figure this out and I am at an absolute loss.
I am working with the JUCE library and have modified one of their sample projects. My goal is to have a very simple Android app that is written in C++ and then ported to Android. I need a function in C++ that I can call that will then call a function on the Android side that will return my heap size and other characteristics to my C++ code so that I can manage memory there.
If anyone has a simple solution that would be amazing. Right now my current snag is this:
char const buf[] = "From JNI";
jstring jstr = env->NewStringUTF(buf);
jclass clazz = env->FindClass("android/os/Debug");
But I keep getting an error saying that 'NewStringUTF' is not a _JNIEnv member... but if I right click on the method and jump to the definition, I see it in my jni.h file... any suggestions? I'm working in Xcode by the way...
Is it plain C, not C++? Perhaps your file has a .c extension.
If it's plain C it should be
JNIEnv* env;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
(*env)->NewStringUTF(env, buf);
I have integrated two native libraries (.so ) in my application. The libraries compile fine and I can load them in my application too. The first time I invoke a native method of a library it works fine, but if I call the same method again in the Activity the application shuts down.
The problem I am facing is exactly the same as mentioned in here :
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call
The solution that works is to invoke the native method in another Activity and shut it down forcefully via System.exit(0). Following the article I tried setting the pointers to NULL of the called method after a successful operation, but this too didn't help me. Also its not possible to unload a library once its loaded by System.loadLibrary().
I want to call the native methods more than once without creating a new Activity. Any ideas how to solve this issue ?
(I FINALLY FOUND A SOLUTION ... HERE IT IS)
Okay, I have finally found a way to resolve this issue. The solution is actually pretty simple. Build another independent native library (utility library) to load and unload the other libraries. What we need to do is use dlopen() and dlclose() in the native method of the utility. We can load the utility library like before via System.loadLibrary().
So in the native method of the utility library what we need to do is:
Use#include <dlfcn.h> // this is required to call dlopen() and dlclose() functions.
Provide handler and function prototype:
void *handle;
typedef int (*func)(int); // define function prototype
func myFunctionName; // some name for the function
Open the library via dlopen() :
handle = dlopen("/data/data/my.package.com/lib/somelibrary.so", RTLD_LAZY);
Get and Call the function of the library:
myFunctionName = (func)dlsym(handle, "actualFunctionNameInLibrary");
myFunctionName(1); // passing parameters if needed in the call
Now that the call is done. Close it via dlclose():
dlclose(handle);
Hope this will help others facing the same issue.
So ... my solution was starting a service that runs the shared library code, this service has a different process name ( you can set it in the Android Manifest ), as it is a different process you can kill it ( Using Process.killProcess(Process.myPid()) when it finishes running, without affecting your application in any way.
Worked very well for me, hope it helps someone else.
As this is the top hit for this issue and as the issue itself still exists, it seems that the approach that ZakiMak shared with us is still the most popular solution.
For others who may want to implement it and would like a little more detail for the latest Android releases, here are some notes I made as I stumbled through this:
Firstly, there is a solution which implements this approach on GitHub now. I have not tried it personally, but I have used it as a reference. It is very useful to see how the Android.mk file is structured and how the library is opened and methods called. Link is here: https://github.com/jhotovy/android-ffmpeg
The path to the native library folder changes over Android releases and it also appears to change every time you run the app (although this may be just in debug mode). Either way, it is best to pass the path in from the calling Java method if possible. For example:
In the Java wrapping class:
import android.content.Context;
import android.util.Log;
public class FfmpegJNIWrapper {
//This class provides a Java wrapper around the exposed JNI ffmpeg functions.
static {
//Load the 'first' or 'outer' JNI library so this activity can use it
System.loadLibrary("ffmpeg_wraper_multi_invoke_jni");
}
public static int call_ffmpegWrapper(Context appContext, String[] ffmpegArgs) {
//Get the native libary path
String nativeLibPath = appContext.getApplicationInfo().nativeLibraryDir;
//Call the method in the first or 'outer' library, passing it the
//native library past as well as the original args
return ffmpegWrapper(nativeLibPath, ffmpegArgs);
}
// Native methods for ffmpeg functions
public static native int ffmpegWrapper(String nativeLibPath, String[] argv);
}
In the 'first' or 'outer' native library:
JNIEXPORT jint JNICALL Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper(JNIEnv *pEnv, jobject pObj, jstring nativeLibPath, jobjectArray javaArgv) {
//Get the second or 'inner' native library path
char* nativePathPassedIn = (char *)(*pEnv)->GetStringUTFChars(pEnv, nativeLibPath, NULL);
char ourNativeLibraryPath[256];
snprintf(ourNativeLibraryPath, sizeof (ourNativeLibraryPath), "%s%s", nativePathPassedIn, "/libffmpeg_wraper_jni.so"); //the name of your ffmpeg library
//Open the so library
void *handle;
typedef int (*func)(JNIEnv*, jobject, jobjectArray);
handle = dlopen(ourNativeLibraryPath, RTLD_LAZY);
if (handle == NULL) {
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "could not open library: %s", dlerror());
printf("Could not dlopen(\"libbar.so\"): %s\n", dlerror());
return(-1);
}
//Call the ffmpeg wrapper functon in the second or 'inner' library
func reenterable_ffmpegWrapperFunction;
reenterable_ffmpegWrapperFunction = (func)dlsym(handle, "Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper");
reenterable_ffmpegWrapperFunction(pEnv, pObj, javaArgv); //the original arguments
//Close the library
dlclose(handle);
// return
return(1);
}
The Android.mk file is a little 'flaky' to put it politely. Because you are building two separate libraries in one Android.mk file, this may be a little more complex that other NDK make files so if you get some strange errors do some searching before you start taking your project apart. For example: https://stackoverflow.com/a/6243727/334402