Sloppy's Blog

AndroidJava调用C++,及C++调用Java

正常的Android开发过程中,难免会出现需要大量的计算,这时候,我们可能就会想到,需要提高计算的速度,这里可能想到利用c/C++,来提高运算速度,那我们这时候,就需要接触到这方面的相关知识了,下面我们作一下简单的实例测试,需要用的工具,包括Android SDK,NDK

创建项目

首先我们创建一个简单的Android项目,然后在layout布局文件上,加一个简单的textview,取名ID为,labelTxt,设置字内容为HelloAndroid.在MainActivity里定义一个方法方法,同时添加载入SO文件的逻辑,内容如下 :

1
2
3
4
public native String stringFromJNI();
static{
System.loadLibrary("jni-test"); // 为什么叫jni-test,跟后面的MK文件内容配置
}

在onCreate方法里添加如下内容:

1
2
3
4
5
String str = "Form Assets so:"+stringFromJNI();
TextView txt = (TextView)this.findViewById(R.id.labelTxt);
if(txt!=null){
txt.setText(str);
}

Java调用C++

这里,我自己手动编写CPP(com_nikoer_helloandroidapp_MainActivity.cpp)文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <jni.h>
#include <android/log.h>
#include <string>
#include "JniHelper.h"
#define LOG_TAG "MAIN"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_nikoer_helloandroidapp_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz){
std::string t="JNI Value";
return env->NewStringUTF(t.c_str());
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
LOGD("JNI_OnLoad");
return JNI_VERSION_1_4;
}
}

这里我用的的是C++编译,所以用到了extern “c”

在项目根目录下建一个jni目录,把CPP文件复制进去,在jni目录下编写Android.mk文件,跟Application.mk文件,
Android.mk:

1
2
3
4
5
6
7
8
9
10
11
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -landroid
LOCAL_LDLIBS += -llog
LOCAL_MODULE := jni-test
LOCAL_SRC_FILES := com_nikoer_helloandroidapp_MainActivity.cpp
include $(BUILD_SHARED_LIBRARY)

Application.mk:

1
2
3
4
5
6
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char
APP_LDFLAGS := -latomic
APP_ABI := armeabi

在jni目录下,运行命令行,输入:ndk-build (当前前掉得设置ndk的环境变量,才可以运行ndk-build命令),用eclipse刷新项目,运行目录。是不是发布HelloWorld变成了JNI Value.

C++调用Java

这里我们封闭一个类出来JniHelper,内容如下:
JniHelper.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include <jni.h>
typedef struct JniMethodInfo_
{
JNIEnv * env;
jclass classID;
jmethodID methodID;
} JniMethodInfo;
class JniHelper{
public:
static void setJavaVM(JavaVM *vm);
static JNIEnv* getEnv();
static bool getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName,const char *paramCode);
private:
static JavaVM *pvm;
};

JniHelper.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "JniHelper.h"
#include <android/log.h>
#define LOG_TAG "JniHelper"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
JavaVM* JniHelper::pvm=nullptr;
void JniHelper::setJavaVM(JavaVM *vm){
pvm = vm;
}
JNIEnv* JniHelper::getEnv(){
JNIEnv* env=nullptr;
pvm->GetEnv((void**)&env,JNI_VERSION_1_4);
return env;
}
bool JniHelper::getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className,const char *methodName, const char *paramCode) {
if ((nullptr == className) ||
(nullptr == methodName) ||
(nullptr == paramCode)) {
return false;
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
LOGE("Failed to get JNIEnv");
return false;
}
jclass classID = env->FindClass(className);
if (! classID) {
LOGE("Failed to find class %s", className);
env->ExceptionClear();
return false;
}
jmethodID methodID = env->GetStaticMethodID(classID, methodName, paramCode);
if (! methodID) {
LOGE("Failed to find static method id of %s", methodName);
env->ExceptionClear();
return false;
}
methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;
}

修改入口文件com_nikoer_helloandroidapp_MainActivity.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_nikoer_helloandroidapp_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz){
std::string t="JNI Value";
// 开始由C++ 调用Java
JniMethodInfo minfo;
bool result = JniHelper::getStaticMethodInfo(minfo,"com/nikoer/helloandroidapp/MainActivity","getTime","()Ljava/lang/String;");
if(result){
jstring jstr = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);
t = "JNI:"+std::string((char*)env->GetStringUTFChars(jstr, 0));
}
return env->NewStringUTF(t.c_str());
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
LOGD("JNI_OnLoad");
JniHelper::setJavaVM(vm);
return JNI_VERSION_1_4;
}
}

修改Android.mk:

1
2
3
4
5
6
7
8
9
10
11
12
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -landroid
LOCAL_LDLIBS += -llog
LOCAL_MODULE := jni-test
LOCAL_SRC_FILES := com_nikoer_helloandroidapp_MainActivity.cpp \
JniHelper.cpp
include $(BUILD_SHARED_LIBRARY)

修改JavaActivity,添加如下方法

1
2
3
public static String getTime() {
return String.valueOf(System.currentTimeMillis());
}

运行:ndk-build,再用Eclipse刷新项目,运行项目,是不是变成时间字符串了

源文件参考:下载