> 创建步骤:
1)在 app/src 的目录下新建 Jni 文件夹用来写C++代码
java调用C++的代码,需要把C++的代码编译成动态库,然后java调用,需要遵循JNI规范。
那么这样C++的函数名称就要遵循JNI的规范。
2)在main/java/包名 的目录下新建java文件Jni,用来调用C++函数
3)写java文件
4)写C++文件
hellloworld.cpp
#include <jni.h>
#include <stdio.h>
void foo()
{
printf("hello world\n");
}
// 这个函数名是JNI规范的
// env表示Java虚拟机
// obj表示Java对象,在具体实现时表示调用这个函数的对象
// extern 要求编译器不要对这个函数进行表名,否则java虚拟机找不到该函数
extern "C" void Java_com_example_administrator_myapplication_Jni_foo(JNIEnv *env, jobject obj)
{
// 在这个里面可以调用自己想要写的C++代码
foo();
}
5)用NDK来编译C++代码,使其能够生成java能够调用的动态库(makefile)
在app/src/Jni (C++代码同级目录)目录下新建一个Android.mk 文件用来编译C++代码
Android.mk
# 设置路径是当前路径
LOCAL_PATH:=$(call my-dir)
# 清除变量
include $(CLEAR_VARS)
# 需要参与编译的源代码文件
LOCAL_SRC_FILES:= helloworld.cpp
# 目标文件 要生成的动态库
LOCAL_MODULE:= mylib # libmylib.so
# andriod log库
LOCAL_LDLIBS += -llog
# 目标文件的类型(表示要生成动态库)
include $(BUILD_SHARED_LIBRARY)
# BUILD_STATIC_LIBRARY
6)运行Android.mk文件 在当前文件目录cmd下:ndk-build,然后在libs文件夹里面有生成的
相应的动态库。
7)在java文件的的Jni类中要加载前面生成的动态库
8)这个时候java文件还是不能调用C++文件,原因是接下来还要把JNI的库加入到项目工程里面去
> 右键项目(Android模式)> Link C++ Project with Gradle
Gradle: 可以看做是C++的makefile,是用来管理和构建java程序的
至此java文件的Jni类中可以调用foo函数了。
9)接着要生成Android应用程序的包,工具栏>Build>Build APK
10)打印调试信息,先编写C++文件
#include <jni.h>
#include <stdio.h>
// 如果希望打印调试信息
#include <Android/log.h>
static const char *tag = "mylibtag";
//为了方便打印
#define MyLog(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, tag, fmt, ##__VA_ARGS__);
void foo()
{
//__android_log_print(ANDROID_LOG_ERROR, tag, "helloworld: %d", 100);
MyLog("hello world: %d", 100);
printf("hello world\n");
}
// 这个函数名是JNI规范的
// env表示Java虚拟机
// obj表示Java对象,在具体实现时表示调用这个函数的对象
// extern 要求编译器不要对这个函数进行表名,否则java虚拟机找不到该函数
extern "C" void Java_com_example_administrator_myapplication_Jni_foo(JNIEnv *env, jobject obj)
{
// 在这个里面可以调用自己想要写的C++代码
foo();
}
extern "C" jint Java_com_example_administrator_myapplication_Jni_add(JNIEnv *env, jobject obj, jint a, jint b)
{
MyLog("add is = %d", a + b);
return a + b;
}
java文件 Jni类:
要在Activity页面调用函数:
Jni jni = new Jni();
jni.foo();
jni.add(10, 20);
11)如果要打印出字符串的话
extern "C" void Java_com_example_administrator_myapplication_Jni_print(JNIEnv *env, jobject obj, jstring content)
{
{
// 要把java的字符串对象转换成C语言的字符串
MyLog("print content is %s", J2C(env, content).c_str());
}
// 要加这个函数
string J2C(JNIEnv* env, jstring jstr)
{
string ret;
// jstr ==> ret
// 调用Java的函数实现该功能:C++怎么调用Java函数
// C++中的字符串用UTF8格式,Java是Unique格式
// C++调用Java函数
// 1. 获得函数的类(String)
// 2. 获得函数地址(getBytes)
// 3. 调用该函数
jclass stringClass = env->FindClass("java/lang/String");
// byte[] getBytes(String)
jmethodID getBytesID = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
jstring arg = env->NewStringUTF("UTF-8");
// 调用函数
// byte[]
// data中保存的是UTF-8编码的字符串
jbyteArray data = (jbyteArray)env->CallObjectMethod(jstr, getBytesID, arg);
// 获取data的长度,将data内容获取出来,拷贝到一个buff中
// 从jbytes中,提取UTF8格式的内容
jsize byteLen = env->GetArrayLength(data);
jbyte* JBuffer = env->GetByteArrayElements(data, JNI_FALSE);
// 将内容拷贝到C++内存中
if(byteLen > 0)
{
char* buf = (char*)JBuffer;
copy(buf, buf+byteLen, back_inserter(ret));
}
// 释放
env->ReleaseByteArrayElements(data, JBuffer, 0);
return ret;
}
> C++文件 helloworld.cpp
#include <stdio.h>
#include <jni.h>
#include <android/log.h>
#include <string>
#include "cn_itcast_firstproject_Jni.h"
using namespace std;
static const char* tag = "mylibtag";
#define MyErr(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, tag, fmt, ##__VA_ARGS__)
#define MyWarn(fmt, ...) __android_log_print(ANDROID_LOG_WARN, tag, fmt, ##__VA_ARGS__)
void foo()
{
//__android_log_print(ANDROID_LOG_ERROR, tag, "helloworld %d", 100);
MyErr("hello world %d", 100);
}
string J2C(JNIEnv* env, jstring jstr)
{
string ret;
// jstr ==> ret
// 调用Java的函数实现该功能:C++怎么调用Java函数
// C++中的字符串用UTF8格式,Java是Unique格式
// C++调用Java函数
// 1. 获得函数的类(String)
// 2. 获得函数地址(getBytes)
// 3. 调用该函数
jclass stringClass = env->FindClass("java/lang/String");
// byte[] getBytes(String)
jmethodID getBytesID = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
jstring arg = env->NewStringUTF("UTF-8");
// 调用函数
// byte[]
// data中保存的是UTF-8编码的字符串
jbyteArray data = (jbyteArray)env->CallObjectMethod(jstr, getBytesID, arg);
// 获取data的长度,将data内容获取出来,拷贝到一个buff中
// 从jbytes中,提取UTF8格式的内容
jsize byteLen = env->GetArrayLength(data);
jbyte* JBuffer = env->GetByteArrayElements(data, JNI_FALSE);
// 将内容拷贝到C++内存中
if(byteLen > 0)
{
char* buf = (char*)JBuffer;
copy(buf, buf+byteLen, back_inserter(ret));
}
// 释放
env->ReleaseByteArrayElements(data, JBuffer, 0);
return ret;
}
// JNI规范规定的
// env表示Java虚拟机
// obj表示Java对象
// extern C要求编译器不要对该函数进行变名,否则java虚拟机找不到该函数
void Java_cn_itcast_firstproject_Jni_foo(JNIEnv* env, jobject obj)
{
foo();
}
extern "C" jint Java_cn_itcast_firstproject_Jni_add(JNIEnv* env, jobject obj, jint a, jint b)
{
MyErr("add is called %d", a+b);
return a + b;
}
extern "C" void Java_cn_itcast_firstproject_Jni_print(JNIEnv* env, jobject obj, jstring content)
{
// 把Java的字符串对象转换成C类型字符串
MyErr("content is %s", J2C(env, content).c_str());
}
这里的string头文件会显示没有加载,那么需要在C++文件同目录下新建Application.mk
内容为:
APP_STL := stlport_static
> java文件 Jni.java
package com.example.administrator.myapplication;
public class Jni {
static{
System.loadLibrary("mylib");
}
// native 关键字表示foo函数不是由java实现,而是由C来实现的
// 当这个函数被调用时,java虚拟机会寻找C语言写的foo函数,进行调用
// Jni规范中,java调用C++的代码的文件名为:Java_包名_类名_函数名
// Java_com_example_administrator_myapplication_Jni_foo();
public native void foo();
public native int add(int a, int b);
public native void print(String content);
}