目錄
基本環境
- 開發板: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