如何将C++代码转换为NDK?

3
这段代码是通过C++代码调用Linux命令的,但是如何将其转换为使用NDK的库? 我看到有一些例子只使用了.C文件,并且在Jni中使用的变量与C ++变量不同。 这是我需要将其转换为使用NDK的C++代码。
#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;
}

这个做法可行吗?

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)

假设我的本地方法是exec(char* cmd),用于 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;

  }

如何安排这些文件以获得正确的解决方案?

我怀疑你能否使用 std::string 返回值与 JNI 进行交互。 - πάντα ῥεῖ
2个回答

4

您仍需要带有本地方法声明的MainActivity java代码。我不知道您是否没有发布它,还是忘记实现它了。它应该是这样的:

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);
}

此外,正如g-makulik在评论中指出的那样,您在本地代码中返回了一个c++字符串,但应该返回一个Java字符串。 NDK1.cpp文件应该类似于以下内容:
#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);
}

最后,您应该运行ndk-build脚本为您的代码生成共享库。请确保将NDK1.cpp文件放在与Android.mk文件相同的目录中,在jni文件夹中(假设您不想更改Android.mk文件以查找其他位置的源代码)。
如果您正在使用Linux(我不知道在Windows上如何操作),则应该在您的Android.mk所在的终端窗口中运行命令'ndk-build'。如果一切顺利,您将不会收到任何错误消息,并且.so文件将被复制到您项目的libs文件夹中。
更新: 也许您需要添加stl库以使用string类。您可以尝试在与Android.mk文件相同的目录中添加一个Application.mk文件,并将其内容设置为:
APP_ABI := armeabi armeabi-v7a
APP_STL := gnustl_static

好的,我已经编写了MainActivity,但C++代码和.mk文件怎么样?都正确吗? - Hana90
@Dondon 我编辑了我的帖子,请尝试运行ndk-build并查看发生了什么。如果您遇到一些错误,可以在此处发布它们以查看问题。 我不确定你正在使用的这个popen是否会起作用,无论如何... - Esparver
谢谢@Esparver,我会尝试这段代码。顺便说一下,我不需要在.cpp文件中使用这个形式: - Hana90
extern "C" { JNIEXPORT void JNICALL jstring Java_com_mindtherobot_samples_ndkfoo_MainActivity_exec(JNIEnv* env, jjobject javaThis , Jchar* cmd);}; - Hana90
我在聊天,你在哪里? - Hana90
显示剩余6条评论

0
Android不允许直接从内部或外部存储器中读取文件。这就是为什么所有使用Android操作系统的人都会阻止安全漏洞。如果您想使用C++本地代码读取文件,则必须从C++中使用Java API调用。
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);

就是这样。你还必须从java中传递assetManager。

public native void readTxtFile(AssetManager assetManager);

并使用此函数获取资源

AssetManager  assetMgr; //define this as global variable

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

同时,您还必须在Android.mk(Eclipse)或Build.gradle(Android Studio)中的android{}标签内添加链接器"log"和"android"库。

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

如果你在Android Studio中使用外部的CMakeList.txt文件

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})

而且您还必须像这样声明Jni c++函数

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

不要忘记在主文件夹中创建资产文件夹,因为您的文件(textfile.txt)位于资产文件夹中,而AssetManager对象从那里读取。 就是这样。我花了两天时间编写这段代码。享受并支持开源 ;)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接