Android Studio開發NDK代碼

基本環境

  • 開發板: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。

三、在機器上執行測試

  1. 關閉seLinux。通過AS來調試app,它的域名爲u:r:untrusted_app:s0,受 selinux 的權限控制,是不允許訪問虛擬文件系統sysfs的,所以通過 如下adb命令控制手機 selinux 進入寬容模式。正常生產狀態開啓 selinux ,要想該app運行正常,需要對該 app 使用 platform key簽名,然後配置 selinux 給予platform_app 訪問 sysfs文件系統的權限。
hikey960:/ # setenforce 0
  1. 使用Android Studio進行調試:
               在這裏插入圖片描述
  2. 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章