Hana90
Hana90

Reputation: 925

how to convert C++ code for NDK?

this code invoked Linux command by using C++ code, but how to convert it to use it as lib via NDK? i see examples that use just .C files, and the variables used by Jni that different than C++ variable.

this c++ code that i need to convert it to use it then with NDK

#include <string>
#include <iostream>
#include <stdio.h>

std::string exec(char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
    if(fgets(buffer, 128, pipe) != NULL)
        result += buffer;
}
pclose(pipe);
return result;
}

is this suitable to do?

in Android.mk

LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)

 # Here we give our module name and source file(s)
 LOCAL_MODULE    := NDK1
 LOCAL_SRC_FILES := NDK1.cpp
include $(BUILD_SHARED_LIBRARY)

suppose my native method is exec(char* cmd), for NDK1.cpp

#include <string>
#include <iostream>
#include <stdio.h>
#include <jni.h>

  jstring Java_com_mindtherobot_samples_ndkfoo_MainActivity_exec(JNIEnv*     env, jobject javaThis , Jchar* cmd) {

......... same code above
 return result;

  }

how to arrange these files to have a correct solution ?

Upvotes: 3

Views: 1992

Answers (2)

Ottoman Empire
Ottoman Empire

Reputation: 231

Android not allows to read files directly from internal or external storage. This is why all people using Android operating system because it blocks security holes. İf you want to read files with c++ native code you must use Java Api calls from C++.

AAssetManager* mgr = AAssetManager_fromJava(env,assetManager);
AAsset* asset = AAssetManager_open(mgr,"textfile.txt", AASSET_MODE_UNKNOWN);

if (NULL == asset) {

    return; //exit with error not loaded
}
long size = AAsset_getLength(asset);
char* buffer = (char*) malloc (sizeof(char)*size);
AAsset_read (asset,buffer,size);
AAsset_close(asset);
free(buffer);

That is it. You must also pass assetManager from java

public native void readTxtFile(AssetManager assetManager);

and also take resources with this function

AssetManager  assetMgr; //define this as global variable

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
assetMgr = getResources().getAssets(); //very important dont forget!
readTxtFile(assetMgr);
}

and you must also add linker "log" and "android" libs in your Android.mk in Eclipse or in Build.gradle(Android Studio) inside android{} tag

ndk {
        moduleName "your_module_name"
        ldLibs "android","log"
        //ldLibs.add("android")
    }

if you use external CMakeList.txt in Android Studio

find_library( # Sets the name of the path variable.
          android-lib

          # Specifies the name of the NDK library that
          # you want CMake to locate.
          android )
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 )

target_link_libraries( native-lib 
                   ${log-lib}
                    ${android-lib})

and u also must declare Jni c++ function like this

void Java_com_example_lenovo_firstopencv_MainActivity_salt(JNIEnv *env,
jobject instance,jobject assetManager) { /*copy above c++ code to here*/}

Dont forget to create assets folder in to main folder because your file(textfile.txt) is located in assets folder and assetmanager object reads from there. That is it. I spent 2 days for write this code. enjoy and support OpenSource ;)

Upvotes: 0

Esparver
Esparver

Reputation: 1555

You still need the MainActivity java code with the native method declaration. I don't know if you didn't post that or you forgot to implement it. it should be something like this:

public class MainActivity extends Activity
{

    /*Don't forget to load the library!!*/
    static {
        System.loadLibrary("NDK1");
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // your initialization here...
    }

    public native String exec(String cmd);
}

In addition, as g-makulik pointed in a comment, you are returning a c++ string in your native code, but you should return a Java string. The NDK1.cpp file should be something similar to this:

#include <string>
#include <iostream>
#include <stdio.h>
#include <jni.h>

std::string exec(char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
        result += buffer;
    }
    pclose(pipe);
    return result;
}

jstring Java_com_mindtherobot_samples_ndkfoo_MainActivity_exec(JNIEnv* env, jobject javaThis , Jchar* cmd) {
    std::string result = exec(cmd); 
    return (*env)->NewStringUTF(env, result);
}

Finally, you should run the ndk-build script to generate the shared library for your code. Be sure to put the NDK1.cpp file in the same directory as the Android.mk file, in the jni folder (assuming you don't want to change your Android.mk file to look for the sources somewhere else). Suposing you are using linux (I don't know how it works for Windows), you should open a terminal where you have your Android.mk. There you must run the command 'ndk-build'. If everything is fine you won't get any errors, and the .so file will be copied in the libs file of your project.

UPDATE: Maybe you need to add the stl library to use the string class. You can try to add an Application.mk file in the same directory as the Android.mk with this content:

APP_ABI := armeabi armeabi-v7a
APP_STL := gnustl_static

Upvotes: 4

Related Questions