在工作中有用过JNI调用, 虽然实际项目中有用过,但是却从来没有去做知识的梳理。趁现在比较不忙,做下知识的梳理。
一、什么是JNI
百度百科的解释:JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。
在工作中,有时候需要调用到C/C++的代码,比如第三方库FFmpeg,或者调用一些由C/C++写的方法,都需要用到JNI。
二、Android studio JNI开发方法
1.声明Native方法
package cn.zzw.jnidemo1.jni;
public class JniTools {
public static native String getMessage(String name);
}
2.生成class文件
利用AS的Terminal,到文件所在的目录下,执行 javac JniTools.java ,生成class文件
3.通过javah生成头文件
这个必须是在app\src\main\java 目录下去执行 javah -jni cn.zzw.jnidemo1.jni.JniTools,这里是 javah -jni +包名+类名。
如果在其他目录下执行,例如如下两种错误:
在main目录下创建jni文件夹并将.h文件剪切到该文件夹
.h文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class cn_zzw_jnidemo1_jni_JniTools */
#ifndef _Included_cn_zzw_jnidemo1_jni_JniTools
#define _Included_cn_zzw_jnidemo1_jni_JniTools
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: cn_zzw_jnidemo1_jni_JniTools
* Method: getMessage
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_cn_zzw_jnidemo1_jni_JniTools_getMessage
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
4.在jni目录下创建.c文件
//
// Created by zuowe on 2019/5/23.
//
#include "cn_zzw_jnidemo1_jni_JniTools.h"
JNIEXPORT jstring JNICALL Java_cn_zzw_jnidemo1_jni_JniTools_getMessage(JNIEnv *env, jclass jc, jstring jname)
{
return (*env)->NewStringUTF(env,"I am from jni libs!");
}
5.在jni目录创建Android.mk和Application.mk
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := myjni
LOCAL_SRC_FILES := toolsfromc.c
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_MODULES := myjni
APP_ABI := all
6.修改app下的build.gradle
ndk{
moduleName "myjni"
// abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定的三种abi体系下的so库
}
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDir "src/main/libs"
}
7.在项目的gradle.properties中添加如下
android.useDeprecatedNdk=true
8.配置并执行ndk-build
配置:
执行:
在jni右键--》External Tools--》ndk-build
结果:
生成如下.so文件
9.调用jni
10.显示结果:
11.上面的方式是Java调用C,再来个C调用Java的例子
Java类:
package cn.zzw.jnidemo1.jni;
import android.content.Context;
import android.widget.Toast;
public class ToastTools {
static {
//加载本地库
System.loadLibrary("myjni");
}
private Context mContext;
public ToastTools(Context mContext) {
this.mContext = mContext;
}
public void show(String msg) {
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
public native void callToastShow(String msg);
}
12. C调用Java例子:c文件
#include "cn_zzw_jnidemo1_jni_ToastTools.h"
JNIEXPORT void JNICALL Java_cn_zzw_jnidemo1_jni_ToastTools_callToastShow
(JNIEnv *env, jobject obj, jstring jmsg)
{
jclass clazz = (*env)->FindClass(env,"cn/zzw/jnidemo1/jni/ToastTools");
jmethodID mid = (*env)->GetMethodID(env,clazz,"show","(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, obj,mid,jmsg);
}
13.C调用Java例子:在Activity中调用
package cn.zzw.jnidemo1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import cn.zzw.jnidemo1.jni.JniTools;
import cn.zzw.jnidemo1.jni.ToastTools;
public class MainActivity extends AppCompatActivity {
public native void sendMsgToC(String msg);
private TextView mTv;
int i = 0;
private Button mBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ToastTools toastTools=new ToastTools(this);
mTv = findViewById(R.id.mTv);
mTv.setText(JniTools.getMessage("zzw"));
mBtn = findViewById(R.id.mBtn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
i++;
toastTools.callToastShow("i am " + i);
}
});
}
}
14. C调用Java例子:效果