为什么要使用NDK开发:
实际的开发过程中会有业务需要使用Java与C/C++两者之间进行数据交互。
很多的业务里都需要C/C
直达车
如何建立一个JNI工程:
新建工程示例:
next,
next
这样一个新的JNI工程就创建完成
已有工程上 创建JNI
步骤:
第一步:新建一个native调用java类。
第二步:得到native调用java类的.class文件和 .h头文件。
第三步:编写CMakeLists.txt文件。
第四步:定义实现头文件的函数。
第五步:java层调用通过native的java方法 调用 JNI当中的函数。实现Java与C/C++交互。
首先新建一个空白工程:空白窗体的布局
第一步:然后在Activity目录下新建一个JavaNativeUtils类:
JavaNativeUtils.java具体代码:
public class JavaNativeUtils {
public static final String LOG_TAG = "==JavaNativeUtils.LOG==";
public static final String RETURN_TAG = "==JavaNativeUtils.RETURN==";
/***********************无参数无返回****************************/
public void noParams(){
Log.e(LOG_TAG,"JavaNativeUtils describe() Called!");
}
public static void staticNoParams(){
Log.e(LOG_TAG,"JavaNativeUtils.staticNoParams() Called!");
}
/***********************有参数有返回****************************/
public String hasParams(String param1){
Log.e(LOG_TAG,"JavaNativeUtils javaMethodHasParams(String msg) is Called!");
return LOG_TAG+ " javaMethodHasParams Method return!";
}
public static String staticHasParams(String param1s, int params2){
Log.e(LOG_TAG,"JavaNativeUtils javaStaticHasParams(String msg) is Called!");
return LOG_TAG+ "javaStaticHasParams Method return!!!";
}
/****************************native 方法区********************************/
//java对象 调用Jni无参无返回
public native void JavaCallJniVoid();
//java对象 调用Jni有参无返回
public native void JavaCallJniVoidHasParams(String p1, int p2);
//Java类调用 Jni无参有返回
public static native String JavaStaticCallJniVoid();
//Java类调用 Jni有参有返回
public static native String JavaStaticCallJniVoidHasParams(String p1 ,int[] p2);
}
第二步:写好native方法之后,就需要我们生成对应的.class和.h头文件
生成.class文件:
如果我们直接在当前目录下运行javac JavaNativeUtils.java 肯定会报错:
提示的是编码错误:修改方案如下
javac -encoding UTF-8 JavaNativeUtils.java
这样就可以成功生成.class文件
生成.h文件
javac -h cpp\jni java\com\wcc\jnitest\JavaNativeUtils.java -encoding UTF-8
此时有红色报错,无需吃惊。此文件我们是不需要用来编译的。
在此文件同目录下随便新建一个XXX.cpp文件 引用这个头文件。
第三步:编写CMakeLists.txt代码如下:
cmake_minimum_required(VERSION 3.4.1) #支持最小的版本号3.4.1 /3.10.2
add_library(
# Sets the name of the library.
JavaNUtils #生成的library名称
# Sets the library as a shared library.
SHARED #
# Provides a relative path to your source file(s).
demo.cpp #通过目标cpp文件,或者是文件列表,下放有具体列表方法
#src/main/cpp/nativec.cpp
)
find_library( #查找lib
# Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
#指定用到的系统库或者NDK库或者第三方库的搜索路径,可选。
#LINK_DIRECTORIES(/usr/local/lib)
target_link_libraries( #连接${}里面的 library到 上方的 library
# Specifies the target library.
JavaNUtils
# Links the target library to the log library
# included in the NDK.
${log-lib})
第四步:XXX.cpp这里命名为demo.cpp。代码如下:
#include "com_dywcc_jnitest_JavaNativeUtils.h"
#include <jni.h>
#include "stdio.h"
using namespace std;
#include "android/log.h"
#define LOG_TAG "demo.cpp"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG ,__VA_ARGS__) // //调用Android打印
extern "C"
JNIEXPORT void
JNICALL Java_com_dywcc_testjni_JavaNativeUtils_JavaCallJniVoid(JNIEnv* env,jobject thiz) {
// TODO: 调用JAVA层 无参数无返回值noParams()方法
//1.通过java对象获取所在的类名
jclass j_clz = env->GetObjectClass( thiz);
//2.通过方法名和 类名,以及标记号 获得方法的所在的内存地址
jmethodID j_methodId = env->GetMethodID(j_clz,"noParams","()V");
//3.通过AllocObject(j_clz)函数得到对象. C++没有GC ,所以之后需要手动释放资源
jobject j_obj = env->AllocObject(j_clz);
LOGE("===========JavaCallJniVoid======call noParams ");
//4.通过对象和方法名 调用java层方法
env->CallVoidMethod(j_obj,j_methodId);
}
extern "C"
JNIEXPORT void
JNICALL Java_com_dywcc_testjni_JavaNativeUtils_JavaCallJniVoidHasParams(JNIEnv* env,jobject thiz, jstring p1,jint p2) {
// TODO: 调用Java 无参无返回静态方法staticNoParams()
//1.通过java对象获取其类名
jclass j_clz = env->GetObjectClass( thiz);
//2.通过方法名和 类名,以及标记号 获得方法的所在的内存地址
jmethodID j_methodId = env->GetStaticMethodID(j_clz,"staticNoParams", "()V");
char * pp = (char *)env->GetStringUTFChars(p1,0);
LOGE("===========JavaCallJniVoidHasParams====== call staticNoParams=== %s %d",pp, p2);
//3.通过对象和方法名 调用java层方法
env->CallStaticVoidMethod(j_clz,j_methodId);
}
extern "C"
JNIEXPORT jstring
JNICALL Java_com_dywcc_testjni_JavaNativeUtils_JavaStaticCallJniVoid(JNIEnv * env, jclass clazz) {
// TODO: JNI层调用 java层对象的有参hasParams方法
//1.通过方法名和 类名,以及标记号 获得方法的所在的内存地址
jmethodID j_methodId = env->GetMethodID(clazz,"hasParams","(Ljava/lang/String;)Ljava/lang/String;");
//2.通过类名,分配一个java对象
jobject j_obj = env->AllocObject(clazz);
//定义一个传入的参数变量
char * jni_params = "===jni_prams ===";
jstring params = env->NewStringUTF(jni_params);
LOGE("===========JavaStaticCallJniVoid====== call hasParams");
//3.通过对象和方法名及其参数 调用java层方法
jstring result = (jstring)env->CallObjectMethod(j_obj,j_methodId,params);
return result;
}
extern "C"
JNIEXPORT jstring
JNICALL Java_com_dywcc_testjni_JavaNativeUtils_JavaStaticCallJniVoidHasParams(JNIEnv * env, jclass clazz,jstring p1, jintArray p2) {
// TODO: JNI层调用 java层 有参静态 staticHasParams 方法
//1.通过方法名和 类名,以及标记号 获得方法的所在的内存地址
jmethodID j_methodId = env->GetStaticMethodID(clazz,"staticHasParams","(Ljava/lang/String;I)Ljava/lang/String;");
//定义两个个传入的参数变量
jint params =100;
LOGE("===========JavaStaticCallJniVoidHasParams======call staticHasParams");
//2.通过对象和方法名 调用java层方法
jstring result = (jstring)env->CallStaticObjectMethod(clazz,j_methodId,p1,params);
return result;
}
第五步:Java层调用代码如下
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private JavaNativeUtils utils;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
utils = new JavaNativeUtils();
binding.JavaCallJniVoid.setOnClickListener(this::testClick);
binding.JavaCallJniVoidHasParams.setOnClickListener(this::testClick);
binding.JavaStaticCallJniVoid.setOnClickListener(this::testClick);
binding.JavaStaticCallJniVoidHasParams.setOnClickListener(this::testClick);
}
public void testClick(View view){
switch (view.getId()){
case R.id.JavaCallJniVoid://调用jni无参无返回,jni有log 输出
utils.JavaCallJniVoid();
break;
case R.id.JavaCallJniVoidHasParams://调用jni无参无返回,jni有log 输出
utils.JavaCallJniVoidHasParams("lixiaolong",288888);
break;
case R.id.JavaStaticCallJniVoid://调用jni静态无参有返回,jni有log 输出
String result= JavaNativeUtils.JavaStaticCallJniVoid();
binding.tvStaticNativeInfo.setText(result);
break;
case R.id.JavaStaticCallJniVoidHasParams://调用jni静态有参有返回,jni有log 输出
String re= JavaNativeUtils.JavaStaticCallJniVoidHasParams("zhangsan ",new int[]{2021});
binding.tvStaticNativeInfo.setText(re);
break;
}
}
}
activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.dywcc.testjni.MainActivity">
<TextView
android:id="@+id/tv_native_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="非静态native:Jni调用无参函数"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/JavaCallJniVoid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CallJniVoid"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_native_info" />
<Button
android:id="@+id/JavaCallJniVoidHasParams"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CallJniVoidHasParams"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/JavaCallJniVoid" />
<TextView
android:id="@+id/tv_static_native_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="静态native:Jni调用有参函数"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/JavaCallJniVoidHasParams" />
<Button
android:id="@+id/JavaStaticCallJniVoid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CallJniVoid"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_static_native_info" />
<Button
android:id="@+id/JavaStaticCallJniVoidHasParams"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CallJniVoidHasParams"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/JavaStaticCallJniVoid" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
通过 import moudle! 到自己用例demo项目。直接作为新工程打开不能运行。切记是通过导入 import module!!!