Android应用程序通过JNI访问C库,我们要在开发板上控制led,需要实现这几个函数
- JNI文件
ledCtrl(int which,int status)
ledOpen()
ledClose() - HardControl.java java文件
声明native方法 在对应的hardcontrol.c实现对应的C函数
1. 新建HardControl.java,编写代码
- 新建HardControl.java
- 打开AS,HardControl.java,编写代码
package com.example.hardlibary; //打包的就是当前的目录
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> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#if 0
typedef struct {
char *name; /* Java里调用的函数名 */
char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
void *fnPtr; /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
jint ledOpen(JNIEnv *env, jobject cls)
{
return 0;
}
void ledClose(JNIEnv *env, jobject cls)
{
}
jint ledCtrl(JNIEnv *env, jobject cls,jint which,jint 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/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文件
- 在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
/home/topeet/Android/iTop4412_ICS_git/prebuilt/ndk/android-ndk-r6/platforms/android-9/arch-arm/usr/lib/libc.so - 重新编译so文件
# arm-none-linux-gnueabi-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/jdk1.6.0_43/include/ -I /usr/lib/jvm/jdk1.6.0_43/include/linux/ -nostdlib /home/topeet/Android/iTop4412_ICS_git/prebuilt/ndk/android-ndk-r6/platforms/android-9/arch-arm/usr/lib/libc.so -I /home/topeet/Android/iTop4412_ICS_git/prebuilt/ndk/android-ndk-r5/platforms/android-9/arch-arm/usr/include /home/topeet/Android/iTop4412_ICS_git/prebuilt/ndk/android-ndk-r5/platforms/android-9/arch-arm/usr/lib/liblog.so
- 在JNI文件中加上打印信息
#include <jni.h> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h> //android offer print library
#if 0
typedef struct {
char *name; /* Java里调用的函数名 */
char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
void *fnPtr; /* C语言实现的本地函数 */
} 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/hardlibary/HardControl");
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map java hello <-->c c_hello */
if ((*env)->RegisterNatives(env, cls, methods, 3) < 0)
return JNI_ERR;
return JNI_VERSION_1_4;
}
- 编译生成so库,出现找不到头文件 的就在安卓源码中find - name “xx”,找到头文件路径,用-I指示,找不到库的,也是在源码中查找,指定需要库的路径
3. 在MainActivity.java
package com.example.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;
import com.example.hardlibary.*;
public class MainActivity extends AppCompatActivity {
boolean ledon = false;
private Button button = null;
private CheckBox checkBoxled1 = null;
private CheckBox checkBoxled2 = null;
private CheckBox checkBoxled3 = null;
private CheckBox checkBoxled4 = null;
class MyButtonListener implements 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);
for(int i= 0;i<4;i++)
HardControl.ledCtrl(i,1);
}
else {
button.setText("ALL ON");
checkBoxled1.setChecked(false);
checkBoxled2.setChecked(false);
checkBoxled3.setChecked(false);
checkBoxled4.setChecked(false);
for(int i= 0;i<4;i++)
HardControl.ledCtrl(i,0);
}
}
}
public void onCheckboxClicked(View view) {
// Is the view now checked?
boolean checked = ((CheckBox) view).isChecked();
// Check which checkbox was clicked
switch(view.getId()) {
case R.id.LED1:
if (checked) {
Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(0, 1);
}
else{
Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(0, 0);
}
break;
case R.id.LED2:
if (checked) {
Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(1, 1);
}
else{
Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(1, 0);
}
break;
case R.id.LED3:
if (checked) {
Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(2, 1);
}
else{
Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(2, 0);
}
break;
case R.id.LED4:
if (checked) {
Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(3, 1);
}
else{
Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();// 显示时间较短
HardControl.ledCtrl(3, 0);
}
break;
// TODO: Veggie sandwich
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.BUTTON);
HardControl.ledOpen(); //在入口函数中调用本地方法打开led,为什么可以直接调用呢个,Ctrl+B 跳转到类定义 因为ledopen是static修饰,静态方法不需要实例化对象
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 MyButtonListener());
/*
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
ledon =!ledon;
if(ledon)
button.setText("ALL OFF");
else
button.setText("ALL ON");
}
});*/
}
}
- 在入口函数中打开led,在按钮或复选框的响应函数中点亮或熄灭led
- 在MainActivity.java中要用HardControl类,所以导入所在包下的文件
在itop4412上运行效果
点击按钮或复选框都会在AndroidStudio中打印信息