一步步教你 ndk /jni 开发,从入门到实践
JNI 开发帮助文档
一. AS环境配置
1. 下载NDK
2.要配置ndk-build 环境变量(下面给出了,cmd方式, 当然也可以 我的电脑 方式添加)
sudo gedit /etc/profile
增加如下内容
export ANDROID_NDK_ROOT=/home/rentianxin/android-sdk-linux/ndk-bundle
export PATH=$PATH:$ANDROID_NDK_ROOT
3.修改build.gradle 增加配置 目的: 开发C/CPP时 有代码提示
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
二. 在AS中使用ndk-build开发JNI示例
说明: Android Studio2.2之前对于JNI开发的支持不是很好,开发一般使用Eclipse+插件编写本地动态库。后面Google官方全面增强了对JNI的支持,包括内置NDK。
1. 新建项目或者 搞一个lib
2. 创建一个类,声明一个native 方法
package com.zhf.ndk;
public class Jni {
// Hello
public static native String getHelloFromJNI();
}
3. 生成头文件 目的是自动生成方法名 复制到CPP 文件里
说明:
实际项目最终可以不包含此头文件,不熟悉C的语法的开发人员,借助于该头文件可以知道JNI的相关语法,
首先引入jni.h,里面包含了很多宏定义及调用本地方法的结构体。重点是方法名的格式。
这里的JNIEXPORT和JNICALL都是jni.h中所定义的宏。
JNIEnv表示一个指向JNI环境的指针,可通过它来访问JNI提供的接口方法。
jclass也是jni.h中定义好的,类型是jobject,实际上是一个不确定类型的指针,这里用来接收Java中的this。
实际编写中一般只要遵循Java_包名_类名_方法名就好了。
头文件只是定义了方法,并没有实现,就像一个接口一样。这里就用C写一个简单的无参的JNI方法。
1. cd 到java文件下
使用Terminal窗口 或者直接在目标文件夹下(E:\application\wiki\lib_ndk\src\main\java),使用cmd命令,这里需要你配置了相关的的环境变量
cd E:\application\wiki\lib_ndk\src\main\java>
2. 生成头文件 目的是自动生成方法名 复制到CPP 文件里
E:\application\wiki\lib_ndk\src\main\java> javah com.zhf.ndk.Jni
此时 会在java 文件夹下面生成一个 com_zhf_ndk_Jni.h 的头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zhf_ndk_Jni */
#ifndef _Included_com_zhf_ndk_Jni
#define _Included_com_zhf_ndk_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_zhf_ndk_Jni
* Method: getHelloFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zhf_ndk_Jni_getHelloFromJNI
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
4.编写mk文件实现JNI方法
说明: 先创建一个jni目录,在src的父目录下创建,也可以在其他目录创建,因为最终只需要编译好的动态库。在jni目录下创建Android.mk Application.mk 和jni.cpp(或者jni.c)文件。
4.1. jni.cpp 中的方法名(Java_com_zhf_ndk_Jni_getHelloFromJNI) 是从 jni.h头文件复制过来的,当然也可以自己写,实际编写中一般只要遵循Java_包名_类名_方法名就好了
#include <jni.h>
extern "C"
jstring Java_com_zhf_ndk_Jni_getHelloFromJNI
(JNIEnv *env, jclass thiz) {
return (env)->NewStringUTF("Hello Jni");
}
4.2. jni.c 会发现C代码比C++ 有两点区别 第一不需要 extern “C”, 第二 指针的使用,需要星号,并在方法中添加env参数.
#include <jni.h>
jstring Java_com_zhf_ndk_Jni_getHelloFromJNI
(JNIEnv *env, jclass thiz) {
return (*env)->NewStringUTF(env, "Hello Jni");
}
4.3. Android.mk LOCAL_MODULE 该值是指的 生成的SO文件的名称. LOCAL_SRC_FILES 是你创建的 C或者 C++文件名
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := zhf
LOCAL_SRC_FILES := jni.cpp
include $(BUILD_SHARED_LIBRARY)
4.4. Application.mk 改文件可以没有. APP_ABI 是指生成几个版本的(cpu架构) os文件(如:arm64-v8a、armeabi-v7a、x86、x86_64). APP_PLATFORM 目标安卓版本
APP_ABI := all
APP_PLATFORM := android-14
5. 使用ndk-build编程生成.so库
切回到jni目录的父目录下,在Terminal中运行ndk-build指令,就可以在和jni目录同级生成一个libs文件夹,里面存放相对应的平台的.so库。同时生成的还有一个中间临时的obj文件夹,和jni文件夹可以一起删除。 打包生成 OS文件 主意: 这里要配置好 ndk-build 环境变量
E:\application\wiki\lib_ndk\src\main> ndk-build
6. 加载.so库并调用方法
在类初始化的时候要加载该.so库,一般会写在静态代码块里。名称就是前面的LOCAL_MODULE。 细心的读者会发现
不仅方法名与前面新建的相同,而且包名和类名也要相同 这是必须的 否则会报错
package com.zhf.ndk;
public class Jni {
static {
System.loadLibrary("zhf");
}
// Hello
public static native String getHelloFromJNI();
}
7. 使用
String strFromJNI = Jni.getHelloFromJNI();
三 实践 (案例: 通过ndk 方式校验应用的报名和签名)
1. 再原先基础上添加三个方法
package com.zhf.ndk;
public class Jni {
// Hello
public static native String getHelloFromJNI();
// 返回私密数据
public static native String getStrFromJNI(Object context);
//验证包名
public static native int verificationPackage(Object context);
//验证签名
public static native int verificationSign(Object context);
}
2. 生成头文件
E:\application\wiki\lib_ndk\src\main\java> javah com.zhf.ndk.Jni
3. 复制头文件的方法名 ,复制到C或者C++ 文件里
extern "C"
jstring Java_com_zhf_ndk_Jni_getHelloFromJNI
(JNIEnv *env, jclass thiz) {
return (env)->NewStringUTF("Hello Jni");
}
extern "C"
JNIEXPORT jint JNICALL Java_com_zhf_ndk_Jni_verificationPackage
(JNIEnv *env, jclass thiz, jobject context_object) {
return 1;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_zhf_ndk_Jni_verificationSign
(JNIEnv *env, jclass thiz, jobject context_object) {
return 1;
}
extern "C"
jstring Java_com_zhf_ndk_Jni_getStrFromJNI
(JNIEnv *env, jclass thiz, jobject context_object) {
return (env)->NewStringUTF(ERROR);
}
4. 直接上完整代码 (CPP版)
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
extern "C"
jstring Java_com_zhf_ndk_Jni_getHelloFromJNI
(JNIEnv *env, jclass thiz) {
return (env)->NewStringUTF("Hello Jni");
}
/**
* 发布的app 签名,只有和本签名一致的app 才会返回 AUTH_KEY
* 这个RELEASE_SIGN的值是上一步用java代码获取的值
*/
// 报名
const char *RELEASE_SIGN = "30****a04";
// 签名 获取繁殖
const char *RELEASE_PACKAGE = "com.zhf.ndk";
extern "C"
JNIEXPORT jint JNICALL Java_com_zhf_ndk_Jni_verificationPackage
(JNIEnv *env, jclass thiz, jobject context_object) {
jclass context_class = env->GetObjectClass(context_object);
//context.getPackageManager()
jmethodID methodId = env->GetMethodID(context_class, "getPackageManager",
"()Landroid/content/pm/PackageManager;");
jobject package_manager_object = env->CallObjectMethod(context_object, methodId);
if (package_manager_object == NULL) {
return 1;
}
//context.getPackageName()
methodId = env->GetMethodID(context_class, "getPackageName", "()Ljava/lang/String;");
jstring package_name_string = (jstring) env->CallObjectMethod(context_object, methodId);
if (package_name_string == NULL) {
return 1;
}
const char *c_package = (char *) env->GetStringUTFChars(package_name_string, 0);
if (strcmp(c_package, RELEASE_PACKAGE) == 0) {
// 返回true
return 0;
} else {
// 返回false
return 1;
}
}
extern "C"
JNIEXPORT jint JNICALL Java_com_zhf_ndk_Jni_verificationSign
(JNIEnv *env, jclass thiz, jobject context_object) {
jclass context_class = env->GetObjectClass(context_object);
//context.getPackageManager()
jmethodID methodId = env->GetMethodID(context_class, "getPackageManager",
"()Landroid/content/pm/PackageManager;");
jobject package_manager_object = env->CallObjectMethod(context_object, methodId);
if (package_manager_object == NULL) {
return 1;
}
//context.getPackageName()
methodId = env->GetMethodID(context_class, "getPackageName", "()Ljava/lang/String;");
jstring package_name_string = (jstring) env->CallObjectMethod(context_object, methodId);
if (package_name_string == NULL) {
return 1;
}
env->DeleteLocalRef(context_class);
//PackageManager.getPackageInfo(Sting, int)
jclass pack_manager_class = env->GetObjectClass(package_manager_object);
methodId = env->GetMethodID(pack_manager_class, "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
env->DeleteLocalRef(pack_manager_class);
jobject package_info_object = env->CallObjectMethod(package_manager_object, methodId,
package_name_string, 64);
if (package_info_object == NULL) {
return 1;
}
env->DeleteLocalRef(package_manager_object);
//PackageInfo.signatures[0]
jclass package_info_class = env->GetObjectClass(package_info_object);
jfieldID fieldId = env->GetFieldID(package_info_class, "signatures",
"[Landroid/content/pm/Signature;");
env->DeleteLocalRef(package_info_class);
jobjectArray signature_object_array = (jobjectArray) env->GetObjectField(package_info_object,
fieldId);
if (signature_object_array == NULL) {
return 1;
}
jobject signature_object = env->GetObjectArrayElement(signature_object_array, 0);
env->DeleteLocalRef(package_info_object);
//Signature.toCharsString()
jclass signature_class = env->GetObjectClass(signature_object);
methodId = env->GetMethodID(signature_class, "toCharsString", "()Ljava/lang/String;");
env->DeleteLocalRef(signature_class);
jstring signature_string = (jstring) env->CallObjectMethod(signature_object, methodId);
const char *c_sign = (char *) env->GetStringUTFChars(signature_string, 0);
//签名一致 返回合法的 api key,否则返回错误
if (strcmp(c_sign, RELEASE_SIGN) == 0) {
// 返回true
return 0;
} else {
// false
return 1;
}
}
const char *ERROR = "error";
const char *ABC = "我是一个秘密";
extern "C"
jstring Java_com_zhf_ndk_Jni_getStrFromJNI
(JNIEnv *env, jclass thiz, jobject context_object) {
if (Java_com_zhf_ndk_Jni_verificationPackage(env, thiz, context_object) == 0 &&
Java_com_zhf_ndk_Jni_verificationSign(env, thiz, context_object) == 0) {
// 返回true
return (env)->NewStringUTF(ABC);
} else {
// 返回false
return (env)->NewStringUTF(ERROR);
}
}
5. 打包生成 OS文件
E:\application\wiki\lib_ndk\src\main> ndk-build
6. 使用
参考文章:
https://www.jianshu.com/p/95ee410b1229
上一篇: Unity改变物体颜色
下一篇: Unity中实现物体动态动画效果