目录
基本环境
- 开发板:hikey960
- 代码:aosp,Android R
- 开发环境:Windows 10 Pro,Android Studio 3.4
演示场景
使用Android studio开发 native代码,通过文件操作函数open()、close()、write()直接访问LED的设备节点,控制LED的亮灭。本文涉及到的源码我已释放到github上面:https://github.com/LuciferZhu/LedControlNDK,请查阅。
一、JAVA声明native接口
在java中添加如下 native 代码的声明,待会我们将 native 代码输出动态库全名为libled_ndkjni.so,所以在下面这个类中静态区执行System.loadLibrary("led_ndkjni"),从而在 ART 中主动加载该动态库。
/* app\src\main\java\com\example\lowlevel\LedNative.java */
package com.example.lowlevel;
public class LedNative {
static {
System.loadLibrary("led_ndkjni");
}
public native int openDev();
public native int closeDev();
public native int devOn();
public native int devOff();
}
二、实现native代码
1.生成与java对接的C/C++函数
ART寻找 native 的实现有两种方式:i. 将JNINativeMethod定义的JNI映射表注册到 ART;ii. 寻找方法 JNIEXPORT [返回类型] JNICALL Java_[包名]_[native声明类]_[类中的方法名]。本文介绍方法②的函数名如何生成。
① 先在Android studio执行“Make Project”,使其将文件LedNative.java编译生成app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\com\example\lowlevelLedNative.class;
② 在目录app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes下打开 cmd ,win10的操作方法是先按住Shift按键后,点鼠标右键并点击如下图中的选项。
然后执行命令:javah com.example.lowlevel.LedNative如下所示;此操作在app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes下生成了与LedNative.java对接的native方法头文件com_example_lowlevel_LedNative.h
2.实现自动生成的函数
① 剪切com_example_lowlevel_LedNative.h到新目录LedControlNDK\app\src\main\jni下面,接下来需要实现头文件中的接口。在该目录下新建C++源文件com_example_lowlevel_LedNative.cpp:
com_example_lowlevel_LedNative.cpp源码实现如下所示:
/* LedControlNDK\app\src\main\jni\com_example_lowlevel_LedNative.cpp */
#include <jni.h>
#define LOG_TAG "ledNative.cpp"
#include <android/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "com_example_lowlevel_LedNative.h"
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static const char *ledDevNode = "/sys/devices/platform/leds/leds/user_led3/brightness";
static int fd;
/*
* Class: com_example_lowlevel_LedNative
* Method: openDev
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_lowlevel_LedNative_openDev
(JNIEnv *, jobject)
{
ALOGD("------%s", __FUNCTION__);
fd = open(ledDevNode, O_RDWR);
if (fd < 0) {
ALOGE("open: %s", strerror(errno));
return -1;
}
ALOGD("------%s() return", __FUNCTION__);
return 0;
}
/*
* Class: com_example_lowlevel_LedNative
* Method: closeDev
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_lowlevel_LedNative_closeDev
(JNIEnv *, jobject)
{
ALOGD("------%s", __FUNCTION__);
close(fd);
return 0;
}
/*
* Class: com_example_lowlevel_LedNative
* Method: devOn
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_lowlevel_LedNative_devOn
(JNIEnv *, jobject)
{
jint ret;
ALOGD("------%s, fd=%d", __FUNCTION__, fd);
ret = write(fd, "255", 4);
if (ret < 0) {
ALOGE("write: %s", strerror(errno));
return -1;
}
return 0;
}
/*
* Class: com_example_lowlevel_LedNative
* Method: devOff
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_lowlevel_LedNative_devOff
(JNIEnv *, jobject)
{
jint ret;
ALOGD("------%s, fd=%d", __FUNCTION__, fd);
ret = write(fd, "0", 2);
if (ret < 0) {
ALOGE("write: %s", strerror(errno));
return -1;
}
return 0;
}
#ifdef __cplusplus
}
#endif
3.定义编译规则
① 在目录LedControlNDK\app\src\main\jni新增文件Android.mk,其内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libled_ndkjni
LOCAL_SRC_FILES:= \
com_example_lowlevel_LedNative.cpp
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES :=
LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
② 在同样的目录下新增文件Application.mk,其内容如下:
APP_ABI := all
4. 链接C++动态库到APK包
如下图所示,点击按钮Link C++ Project with Gradle:
在弹出的窗口中指定使用ndk-build(需要提前装好ndk工具并指定路径),并指定刚才定义好的Android.mk后按OK:
5. 编译工程按 build project。
三、在机器上执行测试
- 关闭seLinux。通过AS来调试app,它的域名为
u:r:untrusted_app:s0,受 selinux 的权限控制,是不允许访问虚拟文件系统sysfs的,所以通过 如下adb命令控制手机 selinux 进入宽容模式。正常生产状态开启 selinux ,要想该app运行正常,需要对该 app 使用 platform key签名,然后配置 selinux 给予platform_app 访问 sysfs文件系统的权限。
hikey960:/ # setenforce 0
- 使用Android Studio进行调试:

- Android机器弹出app主界面如下所示,至此控制 LED 亮灭成功。

参考文献
[1] guaju. Android Studio 生成 so 文件,Your project contains C++ files but it is not using a supported native[EB/OL].简书,2019.03.19