序言: 我们都知道安卓应用最终是要访问到linux驱动程序,如lcd,声卡,串口,led等,那它是怎样实现呢?
这篇文章对这段时间自己学习的一个总结。
系列文章: Android硬件服务框架实例之Vibrator(驱动到应用)
一 java代码直接通过加载C库(C语言实现对驱动open,read,write等后生成库文件工java调用),即通过JNI直接访问访问c函数,这种方法适用于简单的单个app使用的驱动操作,。
下图是通过RegisterNatives函数来直接注册本地函数,也可以通过JNI命名规范来实现。第一种方法效率较第二种方法高。
c代码:
#include <jni.h> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
void c_hello(JNIEnv *env, jobject cls)//此函数可实现对设备文件的操作(open,read,write...)
{
printf("Hello, world!\n");
}
#if 0 //注释
typedef struct {
char *name; /* Java里调用的函数名 */
char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
void *fnPtr; /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
static const JNINativeMethod methods[] = {
{"hello", "()V", (void *)c_hello},
};//hello:java方法,()V:函数签名,可用javap生成,
JNIEXPORT jint JNICALL/* System.loadLibrary */
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, "JNIDemo");//找到对应的类
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map java hello <-->c c_hello 注册本地函数表*/
if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
return JNI_ERR;
return JNI_VERSION_1_4;
}
java代码:
public class JNIDemo {
static { /* 1. load */
System.loadLibrary("native"); /* libnative.so */
}
public native void hello();
public static void main (String args[]) {
JNIDemo d = new JNIDemo();
/* 2. map java hello <-->c c_hello */
/* 3. call */
d.hello();
}
}
二. 硬件访问服务实现对设备的操作,当多个app对同一个设备进行操作的时候,比如多个app对同一个LCD设备通过上面的方法,通过JNI直接访问访问c函数,那显示肯定会乱七八糟。安卓提供了一种机制,即让一个java程序来访问设备-Systemserver.java,这个程序会把访问每个对应设备的JNI的java作为服务加载(addservice())进来,由servic_emanager统一管理,其他应用先获取到设备的服务,然后调用服务相应的方法。如下图通过来增加一个Ledservice来分析硬件访问服务。
1. frameworks/base/services/java/com/android/server/SystemServer.java
main方法-->new SystemServer().run()---->System.loadLibrary("android_servers");
/frameworks/base/services/Android.mk
经过搜索只有/frameworks/base/services/core/jni即这个目录。
2. 加载C库的时候会调用JNI_Onload方法。在/frameworks/base/services/core/jni目录下搜索JNI_Onload关键字,只有onload.cpp函数有调用。
这个函数主要调用各个模块中注册JNI本地方法函数,以VibratorService为例。
会调 用/frameworks/base/services/core/jni/com_android_server_VibratorService.cpp
的register_android_server_VibratorService方法
3. 打开/frameworks/base/services/core/jni/com_android_server_VibratorService.cpp文件,这是硬件服务的JNI层,功能是向上即java注册本地函数,向下加载hal 动态链接库,并访问c函数。
4. 实现hal及设备驱动程序(略),HAL功能是负责访问驱动程序执行硬件操作。
5. frameworks/base/services/java/com/android/server/SystemServer.java.
startOtherServices(); -->添加服务,由service_Manager统一管理。
6.实现VibratorService.java,这个文件就是前面JNI 用到的类。
这个类里实现了java本地方法。查看源码文件位于:
.frameworks/base/services/core/java/com/android/server/VibratorService.java。查看源码声明了本地方法。
到这里一个硬件访问服务就注册完了。
7. 下面来分析app怎么访问VibratorService这个服务,然后调用其本地方法?答案是通过AIDL。在Android 系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行。为此, 我们要先定义好通信接口。
AIDL详解:https://www.jianshu.com/p/d1fac6ccee98
a. 实现接口:
查看framework/base/core/java/android/os/IVibratorService.aidl接口的方法与VibratorService.javajava方法相对应
在frameworks/base/Android.mk
有:core/java/android/os/IVibratorService.aidl \
在frameworks/base/编译会生成IVibratorService.stub。
frameworks/base/services/core/java/com/android/server/VibratorService.java的VibratorService类继承IVibratorService.stub。
b. app:
import android.os.IVibratorService;
private IVibratorService vibratorService = null;
helloService = IHelloService.Stub.asInterface(ServiceManager.getService("vibrator"));
vibrator:名字为SystemServer.java 调用ServiceManger.addvice("vibrator",XX);相对应。
helloService.
相关文章:https://www.jianshu.com/p/fa36e5faea67