NDK 的一点简单应用
在这里记录一下某些内容。
在安卓app里面通过jni执行某些东西,再传回java层来。
比如这样一个简单的小文件
// NdkTools.java
package com.example.jnitest;
public class NdkTools {
public static native String getProp(String key,String def);
static {
System.loadLibrary("ndktools");
}
}
通过native关键字指定这个函数通过jni执行的,下面的 System.loadLibrary 载入 ndktools 这个so。
在app的 res 同级目录新建jni文件夹,我们用的jni相关的文件就放在这里了。
.
├── ./AndroidManifest.xml
├── ./java
│ └── ./java/com
│ └── ./java/com/example
│ └── ./java/com/example/jnitest
│ ├── ./java/com/example/jnitest/MainActivity.java
│ └── ./java/com/example/jnitest/NdkTools.java
├── ./jni
│ ├── ./jni/Android.mk
│ ├── ./jni/NdkTools.c
│ └── ./jni/NdkTools.h
└── ./res
├── ./res/drawable
xxxxx
NDKTools.h 是在 java
目录里面执行 javah -jni com.example.jnitest.NdkTools
生成出来的。
在模块的 build.gradle 的 android 段内添加
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
}
}
这样编译的时候就会通过指定的Android.mk去编译相应的so出来了。
Android.mk内容如下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndktools
LOCAL_SRC_FILES := NdkTools.c
LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog
include $(BUILD_SHARED_LIBRARY)
这个稍微了解一下Android.mk的语法规则就行了。
NdkTools.h内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include <sys/system_properties.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Header for class com_example_jnitest_NdkTools */
#ifndef _Included_com_example_jnitest_NdkTools
#define _Included_com_example_jnitest_NdkTools
#define TAG "jni"
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LOGV(fmt,args...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, "%s(%d):%s() " fmt,__FILENAME__,__LINE__,__func__,##args)
#define LOGD(fmt,args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s(%d):%s() " fmt,__FILENAME__,__LINE__,__func__,##args)
#define LOGW(fmt,args...) __android_log_print(ANDROID_LOG_WARN, TAG, "%s(%d):%s() " fmt,__FILENAME__,__LINE__,__func__,##args)
#define LOGE(fmt,args...) __android_log_print(ANDROID_LOG_ERROR, TAG, "%s(%d):%s() " fmt,__FILENAME__,__LINE__,__func__,##args)
#define LOGI(fmt,args...) __android_log_print(ANDROID_LOG_INFO, TAG, "%s(%d):%s() " fmt,__FILENAME__,__LINE__,__func__,##args)
#define property_get(...) __system_property_get(__VA_ARGS__)
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnitest_NdkTools
* Method: getProp
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jnitest_NdkTools_getProp
(JNIEnv *, jclass, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
这里用宏定义重新简化了一下在c文件内调用安卓的LOG和系统属性获取的函数
NdkTools.c内容如下
#include "NdkTools.h"
JNIEXPORT jstring JNICALL Java_com_example_jnitest_NdkTools_getProp
(JNIEnv *env, jclass obj, jstring key, jstring def)
{
const char *lkey=(*env)->GetStringUTFChars(env,key,0);
LOGI("search property %s",lkey);
char value[81];
memset(value,0,81);
int len = property_get(lkey,value);
if(len==0)
{
return def;
}
else
{
return (*env)->NewStringUTF(env,value);
}
}
上面透露出这几个内容
- 安卓的property最长应该是80个字符,这里可以从aosp的源码看出来
- jstring 转 char 可以用形如
const char *lkey=(*env)->GetStringUTFChars(env,key,0);
的语句来进行。