记录下 Android Studio 嵌入 C 代码的过程,使用 ndk-build.
当前环境:Android Studio 3.51 NDK 20.1
创建 JNI 文件夹
Android结构下,直接在项目右键,选择 New - Folder - JNI Folder ,对话框直接点击 Finish 即可方便地在默认位置创建 jni 文件夹用于存放 c 源码。默认位置在 app/src/main/jni.
创建 Java 类
package cn.sh.changxing.myled;
public class JniLib {
static {
System.loadLibrary("JniLib");
}
public native int led_open();
public native int led_close();
public native int ledIoctl(int num, int en);
}
生成头文件 (.h)
这边可以在External Tools中配置:
每次都这样搞一遍很麻烦,我们可以配置一下扩展工具,这样一劳永逸。
点击File - Setting - Tools - External Tools 打开外部工具配置页,点击 + 新建一个工具。
先起个名字叫,这里叫做 javah.
Program: J D K P a t h JDKPath JDKPath\bin\javah.exe
Arguments: -classpath . -jni -d M o d u l e F i l e D i r ModuleFileDir ModuleFileDir\src\main\jni F i l e C l a s s FileClass FileClass
Working directory: M o d u l e F i l e D i r ModuleFileDir ModuleFileDir\src\main\Java
(上面三行直接复制添加)点击 OK 保存后就新建了一个工具。此时我们右击 JniTest.java,在菜单中选择 External Tools - javah 就可以快速生成头文件并放到 jni 目录。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class cn_sh_changxing_myled_JniLib */
#ifndef _Included_cn_sh_changxing_myled_JniLib
#define _Included_cn_sh_changxing_myled_JniLib
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: cn_sh_changxing_myled_JniLib
* Method: led_open
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_led_1open
(JNIEnv *, jobject);
/*
* Class: cn_sh_changxing_myled_JniLib
* Method: led_close
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_led_1close
(JNIEnv *, jobject);
/*
* Class: cn_sh_changxing_myled_JniLib
* Method: ledIoctl
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_ledIoctl
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
编写 c 代码
//
// Created by cheng on 2019/10/26.
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "cn_sh_changxing_myled_JniLib.h"
#undef TCSAFLUSH
#define TCSAFLUSH TCSETSF
#ifndef _TERMIOS_H_
#define _TERMIOS_H_
#endif
int fd=0;
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_led_1open
(JNIEnv *env, jobject obj)
{
if(fd<=0)fd = open("/dev/leds", O_RDWR|O_NDELAY|O_NOCTTY);
if(fd <=0 )__android_log_print(ANDROID_LOG_INFO, "serial", "open /dev/leds Error");
else __android_log_print(ANDROID_LOG_INFO, "serial", "open /dev/leds Sucess fd=%d",fd);
}
/*
* Class: com_topeet_ledtest_led
* Method: Close
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_led_1close
(JNIEnv *env, jobject obj)
{
if(fd > 0)close(fd);
}
/*
* Class: com_topeet_ledtest_led
* Method: Ioctl
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_cn_sh_changxing_myled_JniLib_ledIoctl
(JNIEnv *env, jobject obj, jint num, jint en)
{
ioctl(fd, en, num);
}
创建 mk 文件
mk 文件用于告诉 ndk-build 该如何编译 c 源码,详情见官方指南。
在 jni 目录下创建 Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -llog
LOCAL_LDLIBS +=-lm
LOCAL_MODULE := JniLib
LOCAL_SRC_FILES =: JniLib.c
include $(BUILD_SHARED_LIBRARY)
其中 LOCAL_SRC_FILES 列出了所有要编译的 c 源码文件
然后创建 Application.mk:
APP_MODULES := JniLib
APP_ABI := all
在 module 的 build.gradle 里,amndroid.defaultConfig 下加入下面配置:
代码:
ndk{
moduleName "JniLib"
//abiFilters \"armeabi-v7a", "x86" //输出指定abi下的so库
}
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDir "src/main/libs"
}
编译:
编译需要使用 ndk-build,其位于 ndk 目录下。如果要直接在命令行使用需要添加环境变量。类似的,为了方便我也添加了一个外部工具。
注意改成你自己的目录。
Program: D:\Android_sdk\ndk\20.0.5594570\build\ndk-build.cmd
Working directory: F:\android_project\Myled
任意找个第地方右击,选择 External Tools - ndk-build 即可编译 c 源码。成功后可以看见创建了 libs 目录,里面包含了不同平台下的 so 文件。
运行
最后修改下 Activity 的代码:
package cn.sh.changxing.myled;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private JniLib jniTest = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jniTest = new JniLib();
jniTest.led_open();
}
public void led1_en(View view) {
//Toast.makeText(this, "test", Toast.LENGTH_SHORT).show();
jniTest.ledIoctl(0, 1);
}
public void led1_off(View view) {
jniTest.ledIoctl(0, 0);
}
public void led2_en(View view) {
jniTest.ledIoctl(1, 1);
}
public void led2_off(View view) {
jniTest.ledIoctl(1, 0);
}
}
参考:https://blog.csdn.net/slaughteryou/article/details/88235000