Android应用程序通过JNI访问C库,我们要在开发板上控制led,需要实现这几个函数
- jni文件
ledCtrl(int which,int status) //控制led亮灭
ledOpen() //
ledClose() - HardControl.java java文件
声明native方法 在对应的hardcontrol.c实现对应的C函数
1. 新建HardControl.java,编写代码
package com.example.ndk.ndk.hardlibrary;
public class HardControl {
public static native int ledCtrl(int which, int status);
public static native int ledOpen();
public static native void ledClose();
static {
try {
System.loadLibrary("hardcontrol");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 打包的就是当前的目录
- 定义一个类HardControl,声明几个native方法,native修饰方法,表示只能调用不能修改,static 表示外部可以直接调用方法
- 在类中定义静态代码块,加载C库,只在第一次被实例化时调用一次
- 选中加载C库语句,Ctrl+Alt+T 增加异常处理代码
2. 编写JNI文件,加载so文件
编写Hardcontrol.c文件
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h> //android offer print library
#if 0
typedef struct {
char *name;
char* signature;
void* fnPtr;
}JNINativeMethod;
#endif
jint ledOpen(JNIEnv* env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native led_open...");
return 0;
}
void ledClose(JNIEnv* env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native led_close...");
}
jint ledCtrl(JNIEnv* env, jobject cls, jint which, jint status)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl:%d,%d", which, status);
return 0;
}
static const JNINativeMethod methods[] = {
{"ledOpen", "()I", (void*)ledOpen},
{"ledClose", "()V", (void*)ledClose},
{"ledCtrl", "(II)I", (void*)ledCtrl},
};
/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* jvm, void* reserved)
{
JNIEnv* env;
jclass cls;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
}
cls = (*env)->FindClass(env, "com/example/ndk/ndk/hardlibary/HardControl");
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map java hello <-->c c_hello */
if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods) / sizeof(methods[0])) < 0)
return JNI_ERR;
return JNI_VERSION_1_4;
}
GCC 生成动态链接库so文件
arm-linux-gcc -fPIC -shared Hardcontrol.c -o libhardcontrol.so
在app/libs下新建armeabi子目录,放入so文件
修改 build.gradle,表示so文件放在lib目录下边
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
如下:
连接开发板,编译运行,错误
Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1936]: 1266 could not load needed library ‘libgcc_s.so.1’ for ‘libhardcontrol.so’ (load_library[1091]: Library ‘libgcc_s.so.1’ not found)
说明libhardcontrol.so依赖于libgcc_s.so.1,看开发板中有没有这个库
- 在Android源码目录下查找libc.so find -name “libc.so”
-nostdlib 表示在生成so文件时,不会自动使用标准libc库,我们可以指定使用哪个libc
arm-linux-gcc -fPIC -shared Hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-7-openjdk-amd64/include -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so -I /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/
注意:这里要加上两个头文件路径-I,不然编译会包如下错误
编译后的放到android studio中,执行报错,dlopen时找不到__android_log_print函数,所以编译so的时候需要添加库,在android源码中搜索:find -name “liblog*”
所以最终编译命令如下:
arm-linux-gcc -fPIC -shared Hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-7-openjdk-amd64/include -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so -I /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/ /work/android-5.0.2/prebuilts/ndk/9/platforms/android-5/arch-arm/usr/lib/liblog.so
3. 在MainActivity.java
package com.example.ndk.ndk.app_0001_leddemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import com.example.ndk.ndk.hardlibrary.HardControl;
public class MainActivity extends AppCompatActivity {
private Button button = null;
private boolean ledon = false;
private CheckBox checkBoxLed1 = null;
private CheckBox checkBoxLed2 = null;
private CheckBox checkBoxLed3 = null;
private CheckBox checkBoxLed4 = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.BUTTON);
// 调用native接口打开led
HardControl.ledOpen();
checkBoxLed1 = (CheckBox)findViewById(R.id.LED1);
checkBoxLed2 = (CheckBox)findViewById(R.id.LED2);
checkBoxLed3 = (CheckBox)findViewById(R.id.LED3);
checkBoxLed4 = (CheckBox)findViewById(R.id.LED4);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ledon = !ledon;
if (ledon)
{
button.setText("ALL OFF");
checkBoxLed1.setChecked(true);
checkBoxLed2.setChecked(true);
checkBoxLed3.setChecked(true);
checkBoxLed4.setChecked(true);
//点亮4个led
for(int i = 0; i < 4; i ++) {
HardControl.ledCtrl(i,1);
}
} else {
button.setText("ALL OFF");
checkBoxLed1.setChecked(false);
checkBoxLed2.setChecked(false);
checkBoxLed3.setChecked(false);
checkBoxLed4.setChecked(false);
for(int i = 0; i < 4; i ++) {
HardControl.ledCtrl(i,0);
}
}
}
});
}
//4个checkbox共用一个onCheckboxClicked方法
public void onCheckboxClicked(View view) {
//是否被按下
boolean checked = ((CheckBox)view).isChecked();
switch (view.getId()) {
case R.id.LED1:
if (checked) {
HardControl.ledCtrl(0,1);
} else {
HardControl.ledCtrl(0,0);
}
break;
case R.id.LED2:
if (checked) {
HardControl.ledCtrl(1,1);
} else {
HardControl.ledCtrl(1,0);
}
break;
case R.id.LED3:
if (checked) {
HardControl.ledCtrl(2,1);
} else {
HardControl.ledCtrl(2,0);
}
break;
case R.id.LED4:
if (checked) {
HardControl.ledCtrl(3,1);
} else {
HardControl.ledCtrl(3,0);
}
break;
}
}
}
在itop4412上运行效果
点击按钮或复选框都会在AndroidStudio中打印信息