概述
一、Java通过Jni调用C/C++
基本流程
代码与分析
(1) java代码
(2) 通过工具生成的头文件
(3) C/C++代码
概述
在安卓开发中,Java调用c/c++以及c/c++回调Java的场景很普遍,例如为了提升软件的运行速度,为了引用现有的c/c++库等。本文初衷是为了记录自己在项目中学到的Java与C互相调用的方法,防止日后忘记。
本文默认你以及会创建简单的Android Studio项目,如果还不会请参考我的这篇博客:
一、Java通过Jni调用C/C++
基本流程
- 编写java文件,并声明native方法
- 生成C/C++头文件
- 实现C/C++方法
代码与分析
(1) java代码
public class Demo {
//这里使用单例模式
//下面这里表示在实例化对象时候,就自动加载jniku库文件,也就是我们c代码编译的.so
static {
System.loadLibrary("demo");
}
public String myname ="daoz";
public String getmyname(){
return myname;
}
private static class Instance {
static Demo instance = new Demo();
}
public static Demo getInstance() {
return Instance.instance;
}
//调用到c/c++的函数,将Java的String 作为参数传递到c/c++
public native void gotoC(String myname);
public int gotoJava(int indata){
System.out.println("the date frem C is :"+indata);
return 0;
}
}
上面这部分代码应该只要会c++,相信不难看懂,因为作者也不太会java,凭借着C++的思维写的,关键是接口:
public native void gotoC(String myname);
这里用native 声明,表示其实现是在C/C++中,不需要在java中实现,调用该方法时候会去C/C++的.so文件里去寻找实现。
(2) 通过工具生成的头文件:
下面这部分代码是自动生成的,可以看到上面我们声明为native的接口,已经在这里有所体现,我们剩下的就是要去实现它。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_java2c_Demo */
#ifndef _Included_com_example_java2c_Demo
#define _Included_com_example_java2c_Demo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_java2c_Demo
* Method: gotoC
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_example_java2c_Demo_gotoC
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
(3) C/C++代码
//
// Created by 24545 on 2020/3/18.
//
#include <com_example_java2c_Demo.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
// 宏定义类似java 层的定义,不同级别的Log LOGI, LOGD, LOGW, LOGE, LOGF。 对就Java中的 Log.i log.d
#define LOG_TAG "jni" // 这个是自定义的LOG的标识
//#undef LOG // 取消默认的LOG
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_example_java2c_Demo_gotoC
(JNIEnv * env, jobject obj, jstring myname){
const char* str;
str = (*env)->GetStringUTFChars(env,myname, 0);
if(str == NULL) {
return ;
}
LOGI("the str from java is : %s",str);
}
运行结果:
2020-03-18 15:52:20.306 13501-13501/? I/art: Not late-enabling -Xcheck:jni (already on)
2020-03-18 15:52:20.421 13501-13501/com.example.java2c I/jni: the str from java is : daoz
至此,Java调用C/C++已经实现,我想你应该也看明白了,是不是很简单?不过如果你是Android Studio小白,相信你想要看到最后这行无比振奋人心的打印,确实是振奋人心,至少对道长来说是的,第一次看到它的时候,道长脑子里突然想起了第一次牵女神的手的时候,感觉是一样一样的。
总结:
如果你是个专业安卓开发人员,那么至此,我想你应该知道怎么通过jni调用C/C++了。如果你是个安卓小白,就像道长一样,我想你如果要看到上面那个打印,你应该先做这些:
- 首先先建立个空的安卓项目,至少把hello word搞出来。
- 先学会组织代码,怎么建jni目录,怎么NDK编译
- 简单的Android.mk的编写
- 以上这些对于一个没有接触安卓的人来说,看起来很难,但从道长的亲身经历来看,可能要想写出很精美的Android.mk很难,但要写出能编译通过的Android.mk,真的很简单,而且都是套路。网上一找一大堆模板。
重点:
要是实在不会,可以直接点击传送门,这个是道长这篇文章的demo,可以直接编译运行,是基于Android Studio3.5 版本的项目,已经测试通过,在这里共享给爱学习的道友们。