在使用Jni之前,我们需要对android studio进行一些配置:
有网的时候:在设置 - plugin - Android NDK Support 勾选--install plugin for disk
然后再打开设置---system setting--android SDK --sdk tool ---勾选上NDK
一、环境配置:
1、解压ndk到指定目录(该目录不能出现中文和空格)
修改系统的环境变量,在path里加上D:\soft\ndk\android_ndk_r10e;
2、创建Project
1)在local.properties文件里添加ndk路径
ndk.dir
=
D
\:\\
soft
\\
ndk
\\
android_ndk_r10e
2)在build里添加
ndk{
moduleName "Hello" //可根据自己取,表示.so文件的前缀
ldLibs "log" //打印日志
abiFilters "armeabi", "armeabi-v7a", "x86" //指定CPU
}
注意://如果你编写的.c文件include的时候报红,在build.gradle里添加
sourceSets.main {
jni.srcDirs 'src/main/source'
}
最后build.gradle的代码:
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.suixi.ndkproject"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk{
moduleName "javaCallC"
ldLibs "log" //打印日志
abiFilters "armeabi", "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.jakewharton:butterknife:6.0.0'
}
3、兼容老的NDK
在gradle.properties里添加:
android.useDeprecatedNdk
=
true
二、
若要在C语言中进行LOG日志
在build.gradle
ndk{
moduleName "cCallJava"
ldLibs "log" //打印日志
abiFilters "armeabi", "armeabi-v7a", "x86"
}
在.c文件中添加如下代码
#include <android/log.h>
#define LOG_TAG "linzhenxiang"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)<span style="font-family:Consolas;background-color:#ffffff"></span>
三、编写java调用c语言的方式
1、写一个java类JNI(类名可自取),用于写native代码
public class JNI {
public native String sayHello();//简单的调用
public native String sayAddHello(String s);//字符串拼接
public native int add(int x,int y);//加法运算
public native int[] increaseArray(int[] arr );//给数组的每个元素都增加10
public native int checkPwd(String s);//检查密码是否正确,并返回值
}
2、动态的生成你所需要的C的头文件的两种方法
1)、在\NDKDEMO\app\src\main\java
执行命令:javah com.suixi.
ndkproject
.
JNI(包名+类名)
2)、在studio底部中的terminal 里执行javah javah com.suixi.
ndkproject
.
JNI
会自动生成如下的代码:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_suixi_ndkproject_JNI */
#ifndef _Included_com_suixi_ndkproject_JNI
#define _Included_com_suixi_ndkproject_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_suixi_ndkproject_JNI
* Method: sayHello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_suixi_ndkproject_JNI_sayHello
(JNIEnv *, jobject);
/*
* Class: com_suixi_ndkproject_JNI
* Method: sayAddHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_suixi_ndkproject_JNI_sayAddHello
(JNIEnv *, jobject, jstring);
/*
* Class: com_suixi_ndkproject_JNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_suixi_ndkproject_JNI_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_suixi_ndkproject_JNI
* Method: increaseArray
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_suixi_ndkproject_JNI_increaseArray
(JNIEnv *, jobject, jintArray);
/*
* Class: com_suixi_ndkproject_JNI
* Method: checkPwd
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_suixi_ndkproject_JNI_checkPwd
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
3、在java中右键新建JniFodler,将上一步生成的.h文件移入改文件夹中
4、rebuild Project 下,会在build-->intermediates生成ndk文件夹
5、在JNI类下加载动态链接库
public class JNI {
{
System.loadLibrary("javaCallC");//动态加载。so文件,只要在用之前加载就成,不一定在这个类里加载
}
public native String sayHello();//简单的调用
public native String sayAddHello(String s);//字符串拼接
public native int add(int x,int y);//加法运算
public native int[] increaseArray(int[] arr );//给数组的每个元素都增加10
public native int checkPwd(String s);//检查密码是否正确,并返回值
}
6、在jni文件夹下新建test.c文件,进行代码编写
#include "test.h"
#include "com_suixi_ndkproject_JNI.h"
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <string.h>
#define LOG_TAG "linzhenxiang"//打印日志的别名,可自己去
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
//jstring to char* jString 转换成 char*指针
char *jstringTochar(JNIEnv *env, jstring jstr) {
char *rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "utf-8");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) (*env)->CallObjectMethod(env, jstr, mid, strencode);
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte *ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen > 0) {
rtn = (char *) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}
JNIEXPORT jstring JNICALL Java_com_suixi_ndkproject_JNI_sayHello
(JNIEnv *env, jobject jobject1) {
// jstring (*NewStringUTF)(JNIEnv*, const char*);
char *s = "I AM FROM C";
return (*env)->NewStringUTF(env, s);
};
/**
* 返回带有参数的字符串
*/
JNIEXPORT jstring JNICALL Java_com_suixi_ndkproject_JNI_sayAddHello
(JNIEnv *env, jobject jobject1, jstring js) {
char *from = jstringTochar(env, js);
char *to = "I am from c";
strcat(from, to);//字符串拼接,会把拼接后的结果放在第一个参数里
return (*env)->NewStringUTF(env, from);
};
/**
* 加法运算
*/
JNIEXPORT jint JNICALL Java_com_suixi_ndkproject_JNI_add
(JNIEnv *env, jobject jobject1, jint x, jint y) {
return x + y;
};
/**
* 给数组的每个元素添加10
*/
JNIEXPORT jintArray JNICALL Java_com_suixi_ndkproject_JNI_increaseArray
(JNIEnv *env, jobject jobject1, jintArray array) {
//jsize (*GetArrayLength)(JNIEnv*, jarray);
jsize size = (*env)->GetArrayLength(env,array);//获取数组的长度
// jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
jint* jint1 = (*env)->GetIntArrayElements(env,array,JNI_FALSE);//获取数组中的元素
int i;
for(i=0;i<size;i++){
*(jint1+i)+=10;
}
return array;
};
/**
* 检查密码是否正确
*/
JNIEXPORT jint JNICALL Java_com_suixi_ndkproject_JNI_checkPwd
(JNIEnv * env, jobject jobject1, jstring js){
char * from = jstringTochar(env,js);
char * origin = "123456";
//extern int strcmp(const char *, const char *) __purefunc;
int code = strcmp(from,origin);//strcmp函数用于比较两个字符串是否相等
LOGE("code = %d",code);
if(code==0){
return 400;
}else{
return 200;
}
};
7、在activity中实例化JNI,并进行调用
四:在C中调用Java的方式
3、在java中右键新建JniFodler,将上一步生成的.h文件移入改文件夹中
4、rebuild Project 下,会在build-->intermediates生成ndk文件夹
定位到:build-->intermediates-->classes-->debug
按住 shift+鼠标右键 ----->点击此处打开命令窗口
执行:javap -s com/suixi/myapplication/JNI.class
生成如下签名:
7、在activity中调用
public class MainActivity extends Activity {
private JNI jni;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
jni = new JNI();
}
@OnClick({R.id.btn1,R.id.btn2,R.id.btn3,R.id.btn4,R.id.btn5})
public void onClick(View v){
switch (v.getId()){
case R.id.btn1:
String result = jni.sayHello();
Toast.makeText(this,result,Toast.LENGTH_LONG).show();
break;
case R.id.btn2:
String result1 = jni.sayAddHello("I am from java");
Toast.makeText(this,result1,Toast.LENGTH_SHORT).show();
break;
case R.id.btn3:
int result2 = jni.add(99,1);
Toast.makeText(this,result2+"==",Toast.LENGTH_SHORT).show();
break;
case R.id.btn4:
int[] arr = {1,2,3,4,5};
jni.increaseArray(arr);
for(int i=0;i<arr.length;i++){
Log.e("iii",arr[i]+"==");
}
break;
case R.id.btn5:
String pwd = "1234566";
int code = jni.checkPwd(pwd);
Toast.makeText(this,code+"--",Toast.LENGTH_SHORT).show();
break;
}
}
}
四:在C中调用Java的方式
1、写一个java类JNI(类名可自取),用于写native代码
public class JNI {
/**
* 当执行这个方法的时候,让C代码调用
* public void helloFromJava()
*/
public native void callbackHelloFromJava();
public void helloFromJava() {
Log.e("TAG", "helloFromJava()");
}
}
2、动态的生成你所需要的C的头文件的两种方法
1)、在\NDKDEMO\app\src\main\java
执行命令:javah com.suixi.
ndkproject
.
JNI(包名+类名)
2)、在studio底部中的terminal 里执行javah javah com.suixi.
ndkproject
.
JNI3、在java中右键新建JniFodler,将上一步生成的.h文件移入改文件夹中
4、rebuild Project 下,会在build-->intermediates生成ndk文件夹
5、在JNI类下加载动态链接库
public class JNI {
{
System.loadLibrary("cCallJava");
}
/**
* 当执行这个方法的时候,让C代码调用
* public void helloFromJava()
*/
public native void callbackHelloFromJava();
public void helloFromJava() {
Log.e("TAG", "helloFromJava()");
}
}
6、编写.c文件
#include "Test2.h"
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
JNIEXPORT void JNICALL Java_com_suixi_myapplication_JNI_callbackHelloFromJava(JNIEnv *env, jobject instance) {
//jclass (*FindClass)(JNIEnv*, const char*);
jclass jclazz = (*env)->FindClass(env,"com/suixi/myapplication/JNI");//第二个参数表示的是JNI所在的包名+类名
//第三个参数表示的是你要调用的java类中的方法,
//第四个参数表示的是签名:定位到JNI.class文件的位置,通过命令行:javap -s com/suixi/myapplication/JNI.class
// 生成签名
jmethodID jmethodID1 = (*env)->GetMethodID(env,jclazz,"helloFromJava","()V");
// jobject (*AllocObject)(JNIEnv*, jclass);
jobject jobject1 = (*env)->AllocObject(env,jclazz);
//通过该方法调用即可
(*env)->CallVoidMethod(env,jobject1,jmethodID1);
};
注意:GetMethodID的签名的生成方法
定位到:build-->intermediates-->classes-->debug
按住 shift+鼠标右键 ----->点击此处打开命令窗口
执行:javap -s com/suixi/myapplication/JNI.class
生成如下签名:
7、在activity中调用
public class MainActivity extends Activity {
private JNI jni;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
jni = new JNI();
}
@OnClick({R.id.bt1})
public void onClick(View v){
switch (v.getId()){
jni.callbackHelloFromJava();
}
}
}
源码下载:下载代码