一、编写目的
本文主要提供一个简单可用的 jni 模板,便于在APK中编写native代码。通过记录,希望能对jni学习有一个更深的认识。
二、APP中的jni模板
1、jni文件在项目中的截图
2、详细步骤
1)、在src/main/ 层,创建jni目录用于存放,native代码
2)、增加Android.mk
# Android.mk for check is rightful board
LOCAL_PATH := $(call my-dir)
# Program
include $(CLEAR_VARS)
LOCAL_MODULE := nativeXXXX
LOCAL_SRC_FILES := nativeXXXX.c
LOCAL_C_INCLUDES += $(LOCAL_PATH) // 配置头文件目录,根据实际修改
LOCAL_LDLIBS := -llog -lz // 添加静态依赖库,编译需要
LOCAL_SHARED_LIBRARIES := libcutils // 添加动态依赖库,读取系统属性需要
include $(BUILD_SHARED_LIBRARY)
3)、增加Application.mk
APP_ABI :=armeabi-v7a // 指定编译哪些CPU架构
4)、增加nativeXXXX.c
//
// Created by LinChengChun on 2018/1/2.
//
#include "nativeXXXX.h"
static const char *ClassName = "com/example/XXXX"; // 包名
jint test(JNIEnv *env, jobject jobj){
char *key = "ro.build.id";
char value[PROP_VALUE_MAX] = {0};
int ret = __system_property_get(key, value);
if(ret <= 0 ){
return -1;
}
ret = strcmp(value, "XXXX");
if(ret != 0){
return -1;
}
return 0;
}
static JNINativeMethod METHODS[]={
{"test","()I",(void *)test}
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
int retVal = -1;
JNIEnv *env;
if ( (*vm)->GetEnv(vm,(void **) &env, JNI_VERSION_1_4) == JNI_OK ) {
jclass clazz = (*env)->FindClass(env,ClassName);
if (clazz != NULL) {
if ((*env)->RegisterNatives(env,clazz, METHODS,sizeof(METHODS) / sizeof(METHODS[0])) >= 0) {
retVal = JNI_VERSION_1_4;
}else{
LOGI("RegisterNatives Subprocess.create method failed!");
}
(*env)->DeleteLocalRef(env,clazz);
}else{
LOGI("className not found!");
}
}
return retVal;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
}
5)、增加nativeXXXX.h
//
// Created by LinChengChun on 2018/1/2.
//
#ifndef XXXX_ANDROID_NATIVEXXXX_H
#define XXXX_ANDROID_NATIVEXXXX_H
#include <jni.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <android/log.h>
#include <sys/system_properties.h>
#define LOG_TAG "NativeXXXX"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif //XXXX_ANDROID_NATIVEXXXX_H
6)、在JAVA层增加native接口类
package com.example;
/**
* Created by LinChengChun on 2018/1/2.
*/
public class XXXX{
public native static int test(); // 在外部可以通过 XXXX.test() 方法调用
static {
System.loadLibrary("nativeXXXX"); // 优先加载动态库
}
}
3、添加NDK编译工具,AndroidStudio中添加 外部编译命令 的方法
在Settings > Tools > External Tools中添加命令行工具(NDK)如下: ndk-build、ndk build clean、javah
如果顺利完成以上步骤,就可以在工程任意位置 右键->NDK->ndk-build 编译生成 so库了
三、在JNI读取系统属性方法
用 objdump 看了一下 libc.so ,找到了其中的函数。如下:
000095f0 g F .text 00000014 __system_properties_init
00009604 g F .text 00000014 __system_property_find
00009618 g F .text 00000014 __system_property_find_nth
0000962c g F .text 00000014 __system_property_get
00009640 g F .text 00000014 __system_property_read
00009654 g F .text 00000014 __system_property_wait
头文件是 system_properties.h ,在 usr/include/sys目录下面。
__system_property_get 可以用来获取一个属性值,函数原型如下:
/* Look up a system property by name, copying its value and a
** \0 terminator to the provided pointer. The total bytes
** copied will be no greater than PROP_VALUE_MAX. Returns
** the string length of the value. A property that is not
** defined is identical to a property with a length 0 value.
*/
int __system_property_get(const char *name, char *value);
读写ro.build.id的方法
char *key = "ro.build.id";
char value[PROP_VALUE_MAX] = {0};
int ret = __system_property_get(key, value);
if(ret <= 0 ){
return -1;
}
四、在JAVA中读取系统属性方法
private static String getSystemProperty(String key, String defaultValue) {
try {
Class<?> clz = Class.forName("android.os.SystemProperties"); // 通过反射实现
Method get = clz.getMethod("get", String.class, String.class);
return (String)get.invoke(clz, key, defaultValue);
} catch (Exception e) {
}
return defaultValue;
}
public static boolean test(){
String value =CurrentClass.getSystemProperty("ro.build.id", null);
if (TextUtils.isEmpty(value)){
return false;
}
if(!"XXXX".equals(value)){
return false;
}
return true;
}
五、把编译生成的so库打包到apk里面去
android {
compileSdkVersion 23
buildToolsVersion '25.0.0'
defaultConfig {
。。。。。。
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
//加载so库目录
sourceSets {
main {
// Tell Android Gradle that no jni code need to compile, so we can avoid Ndk compile task,
// then we will to compile use ndk-build in command-line.
jni.srcDirs = [] // 不依赖 gradle来编译 jni,根据我们的ndk-build来编译
jniLibs.srcDir "src/main/libs"
}
}
}
。。。。。。