序言: 我們都知道安卓應用最終是要訪問到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