最近由于项目的开发需要,需要用到安卓系统,由此开始安卓的一些学习,下面只作为个人的一些记录。
安卓应用是由java编写的,而在android底层,如安卓底层的硬件驱动程序,都是由C/C++编写的,那应用层java是必须通过某种手段才能使用底层提供的硬件资源。毫无疑问,在java看要调用底层C实现的代码,中间必须要有一层接口,那就是JNI。本例要实现的就是JAVA如何访问底层C库。
假设JAVA应用需要实现的功能是控制底层硬件LED设备,包括开,关及控制操作。需要实现下述流程:
1 实现一个JAVA硬件控制类,声明本地的Native方法(操作硬件的接口),并使用static模块加载C的动态库。实现代码如下:
package com.example.zhm.hardlibrary; //声明目录名
public class HardControl{
//声明本地的Native方法
public static native int ledCtrl(int which, int status);
public static native int ledOpen();
public static native void ledClose();
//我们要在这个java程序里面加载C库,定义一个static模块
static {
//用System方法来加载C库
try { //添加捕获异常代码的快捷键为:ctrl+alt+T(前提先用鼠标选中需要捕获的代码)
System.loadLibrary("hardcontrol"); //参数为C库的库名
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意,
加载C库的方法为System.loadLibrary.
上面声明的本地native方法就是要操作底层硬件的几个功能,它是与硬件关联的,既然是Native,自然不是在本地实现,所以,它自然是由C库中实现的。那本地的这些操作硬件的native方法与C库的硬件操作接口是如何关联的呢?请看下面。
2 JAVA本地native方法与C库接口的关联
先上代码再说,以下为C文件,将其命名为:hardcontrol.c
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h> /* liblog */
jint ledOpen(JNIEnv *env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native_ledOpen"); //调用这句就可以打印了。
return 0;
}
void ledClose(JNIEnv *env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native_ledClose"); //调用这句就可以打印了。
}
void 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}, //() inside empty=> no params, 'I' means: return type is int
{"ledClose", "()V", (void *)ledClose},//() inside empty=> no params, 'V' means: return type is void
{"ledCtrl", "(II)I", (void *)ledCtrl}, //()inside two I => means two params, types is int, ouside () is 'I' ==> return type is int
};
/* System.LoadLibrary */
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;
if((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
return JNI_ERR;; //jni version not supported
}
//find the class that realize the local native method: *.java, here is HardControl
//PS:here need to indification the package & class name.
// "." --> "/"
cls = (*env)->FindClass(env, "com/example/zhm/hardlibrary/HardControl");
if( cls == NULL )
{
return JNI_ERR;
}
//find the class then register their methods . Java's method <==correspond with ==> C's method
if((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0 ) // 3: means there are 3 funcs in methods.
{
}
}
首先,
JNINativeMethod 变量methods的第一个元素为java的本地native方法,第二个元素为C库的参数与返回值说明,第三个元素是与本地native方法关联的C函数。由此就完成了它们之间的关联。 除了关联之外,还需要在进行注册,就是它了:
RegisterNatives<span style="font-family: Arial, Helvetica, sans-serif;">(env, cls, methods, sizeof(methods)/sizeof(methods[0]))</span>
3 将hardcontrol.c编译成libhardcontrol.so库:
使用交叉编译工具:arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include
ps:后面的-I是寻找到jni.h的目录,编译出的动态库要么要在/system/lib或/vendor/lib或者放到应用程序中。问题:如何放到java应用中以及让应用能访问C库的方法:
(1)需要在安卓工程下的app/libs/下创建armeabi目录,并将上面so文件拷入此目录下
(2)修改build.gradle文件(这种文件有2个,修改Module:app)这个,在这个文件中增加:
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
上面代码是告诉应用去/app/libs寻找so文件,即so文件的存放位置要告诉应用
(3) 编译后运行,此时不能用模拟器,因为模拟器没有arm指令集,必须用开发板,用USB接上开发板,然后编译烧入。
途中会遇到的一些问题及解决办法:
3 在JNI文件中增加头文件 #include <android/log.h>,然后就可以调用如下语句进行日志打印:
__android_log_print(ANDROID_LOG_DEBUG,"JNI_NFC", "native ledCtrl...:%d, %d", which, status);
但是在编译过程中,需要指定log.h头文件的路径,所以需要在编译选项中添加: -I /home/lxj/rk312x/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/include/
arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so -I /home/lxj/rk312x/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/include/
但是在运行程序过程中会出现如下错误:cannot locate symbol "__android_log_print" referenced by "/data/app/com.reallife.app_nfcdemo_test-2/lib/arm/libhardcontrol.so
说明动态库hardcontrol.so中找不到__android_log_print符号,表明依赖liblog.so库,因为在编译此动态库时还需要指定需要链接的库的路径:如下所示:
arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include -nostdlib /home/lxj/rk312x/prebuilts/ndk/9/platforms/android-9/arch-arm/usr/lib/libc.so -I /home/lxj/rk312x/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/include/ /home/lxj/rk312x/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/liblog.so