Jni学习笔记(二)Java与C++交互,jni学习笔记
分享于 点击 8844 次 点评:224
Jni学习笔记(二)Java与C++交互,jni学习笔记
Java与C++交互
在练习之前先在C层打下log日志。Android NDK中为开发者提供了一些功能,其中就有log日志。具体功能可参考NDK官方参考文档。
使用AndroidStudio创建支持C++的项目,接下来在cpp包中创建一个头文件(创不创建其实都行,就是相当于在Java中写了个工具类)在这里引入log.h头文件。
#ifndef JNITEST2_LOGUTILS_H
#define JNITEST2_LOGUTILS_H
#include <android/log.h>
#define log_i(TAG, arg...) __android_log_print(ANDROID_LOG_INFO,TAG,##arg)
#define log_d(TAG, arg...) __android_log_print(ANDROID_LOG_DEBUG,TAG,##arg)
#define log_w(TAG, arg...) __android_log_print(ANDROID_LOG_WARN,TAG,##arg)
#define log_e(TAG, arg...) __android_log_print(ANDROID_LOG_ERROR,TAG,##arg)
#endif
接下来在源文件中引用,上面的这个头文件。我的头文件名称为LogUtils.h,如果是用AS创建的项目,那么默认的源文件名称应该是native-lib.cpp,在源文件中打印一下log,测试一下log是否好用,代码如下:
#include <jni.h>
#include <string>
#include "LogUtils.h"
using namespace std;
#define TAG "C++"//设置TAG
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_lyan_jnitest2_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobject) {
JNIEnv &jniEnv = *env;
log_i(TAG, "LOG");
log_d(TAG, "LOG");
log_w(TAG, "LOG");
log_e(TAG, "LOG");
return jniEnv.NewStringUTF("Hello from C++");
}
接下来运行下项目,看一下log是否被打印出来。不出意外的情况下应该是能打印出来的,效果应该是这样的:
一、Java与C++方法互调
既然是方法互调,那就需要考虑到类方法(静态方法)和实例方法(对象实例化才能调用的方法)。这里只考虑最基本的功能不考虑资源的释放。
1.实例方法的调用
这里使用Java调用Native方法调用C层,在C层接收到数据后在C层中调用Java实例的方法,代码如下:
Java
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(v -> sendMsgToC("来自JAVA"));
}
//调用C
private native void sendMsgToC(String msg);
//被C调用
public void fromC(String msg) {
Log.i(TAG, msg);
}
}
C++
#include <jni.h>
#include <string>
#include "LogUtils.h"
using namespace std;
#define TAG "C++"//设置TAG
extern "C"
JNIEXPORT void JNICALL
Java_com_lyan_jnitest2_MainActivity_sendMsgToC(JNIEnv *env, jobject object, jstring msg_) {
const char *msg = env->GetStringUTFChars(msg_, 0);
log_i(TAG, "%s", msg);//打印从Java层传过来的值
jclass mainClass = env->GetObjectClass(object);//获取方法ID需要用到这个
//参数一:类、 参数二:方法名 、参数三:方法签名
jmethodID methodID = env->GetMethodID(mainClass, "fromC", "(Ljava/lang/String;)V");
//调用Java实例的方法
env->CallVoidMethod(object,methodID,env->NewStringUTF("from C++"));
}
这里需要注意的就是这个方法签名。这玩意说着比较费劲,看下面这个图大致就能明白咋回事。
接下来看一下运行结果。
2.类方法(静态方法)的调用
这个练习与上面的练习功能基本一致,只不过这里用的是静态方法。
Java
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(v -> sendMsgToC("来自JAVA"));
}
//调用C
private static native void sendMsgToC(String msg);
//被C调用
public static void fromC(String msg) {
Log.i("static", msg);
}
}
C++
#include <jni.h>
#include <string>
#include "LogUtils.h"
using namespace std;
#define TAG "C++"//设置TAG
extern "C"
JNIEXPORT void JNICALL
Java_com_lyan_jnitest2_MainActivity_sendMsgToC(JNIEnv *env, jclass type, jstring msg_) {
const char *msg = env->GetStringUTFChars(msg_, 0);
log_i(TAG, "%s", msg);//打印从Java层传过来的值
//静态方法用 GetStaticMethodID()获取方法ID
jmethodID methodID = env->GetStaticMethodID(type, "fromC", "(Ljava/lang/String;)V");
//在调用Java方法中同样多了Static关键字
env->CallStaticVoidMethod(type, methodID, env->NewStringUTF("from C++"));
}
运行结果如下:
3.注意
以上面的练习为参考,在调用Java的方法需要需要注意类方法与实例方法是不同的。
— | 获取方法ID | 调用Java方法 |
---|---|---|
实例方法 | GetMethodID | CallVoidMethod |
类方法 | GetStaticMethodID | CallStaticVoidMethod |
二、在C层修改Java类的变量
这里也需要注意,获取实例变量和静态变量也是有区别的。规律跟调用方法差不多,这里就直接放代码了。
Java
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
public static String arg1 = "arg1";
private String arg2 = "arg2";
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(v -> beforeChange());
}
//调用C
private native void beforeChange();
//被C调用
public void afterChange() {
Log.w(TAG, "修改变量后");
Log.i(TAG, arg1);
Log.i(TAG, arg2);
}
}
C++
#include <jni.h>
#include <string>
#include "LogUtils.h"
using namespace std;
#define TAG "C++"//设置TAG
extern "C"
JNIEXPORT void JNICALL
Java_com_lyan_jnitest2_MainActivity_beforeChange(JNIEnv *env, jobject instance) {
log_i(TAG,"修改变量前");
jclass type = env->GetObjectClass(instance);
//实例变量
jfieldID jfieldID1 = env->GetFieldID(type,"arg2","Ljava/lang/String;");
jstring jstring1 = (jstring) env->GetObjectField(instance, jfieldID1);
log_w(TAG,"%s",env->GetStringUTFChars(jstring1,JNI_FALSE));//打印变量值
env->SetObjectField(instance,jfieldID1,env->NewStringUTF("实例变量"));//修改变量值
//静态变量
jfieldID jfieldID2 = env->GetStaticFieldID(type,"arg1","Ljava/lang/String;");
jstring jstring2 = (jstring) env->GetStaticObjectField(type, jfieldID2);
log_w(TAG,"%s",env->GetStringUTFChars(jstring2,JNI_FALSE));//打印变量值
env->SetStaticObjectField(type,jfieldID2,env->NewStringUTF("静态变量"));//修改变量值
//修改完属性调用Java层的方法 在Java层打印一下看看 属性是否被修改
jmethodID methodID = env->GetMethodID(type,"afterChange","()V");
env->CallVoidMethod(instance,methodID);
}
运行结果如下。获取变量ID,同样也是用 Javap -s 命令获取的。
相关文章
- 暂无相关文章
用户点评