Jni学习笔记(二)Java与C++交互

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方法
实例方法GetMethodIDCallVoidMethod
类方法GetStaticMethodIDCallStaticVoidMethod

二、在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 命令获取的。
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值