企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 简述 前面的博客中,我们直接添加C lib到APK中,然后使用LoadLibrary加载这个库,同时添加一个class来作为中间层,直接使用这个C库中的native函数来控制硬件,这种做法将硬件与APK牢牢绑定,如果有多个APP来访问同一个硬件就会出现问题,代码也会有很多的重复,在Android中,我们使用Android的SystemServer向ServiceManager来将硬件的功能添加为一个服务,这样当一个APP需要使用硬件的时候就向SystemServer发出请求service服务,然后由ServiceMnager统一提供服务,提供统一的接口与硬件控制,即相当于多添加了一层,从而实现解耦。 ## 详细原理 先看下图(图片来源于韦东山的Android视频资料)中的③②①,按照顺序: 1. SystemServer会加载Cpp lib 1. 在JNI_OnLoad中注册各个Service,SystemServer向ServiceManager添加服务 1. 这些service就包括像串口/LED等硬件相关的服务 ![](https://box.kancloud.cn/2016-05-05_572afc9e48dc5.jpg) 而使用的时候,就是7~5步骤: 1. AddService:SystemServer向ServiceManager添加服务addServeice 1. getservice:通过getservice来从SystemServer注册了的service中获取服务所具有的功能,例如ledctrl 1. 使用Service的方法:APP使用一个Interface(即以i开头的对象)来使用service提供的功能,将服务请求到SystemServer去 APP/SystemServer/ServiceManager三者都是通过Bindler来通讯。 ## 添加Service与使用Service的步骤 ### 添加serviceAIDL文件,生成Interface java文件 因为系统中其他都aidl文件都放在frameworks/base/core/java/android/os下,所有我们也参考其他的文件添加一个ILedService.aidl: ~~~ package android.os; /** {@hide} */ interface ILedService { int ledCtrl(int which, int status); } ~~~ 可以看到这个interface前面有个@hide的修饰,表明是个hide class。 同时还需要将此aidl文件添加到Android.mk(Makefile)中: ~~~ $ git diff Android.mk diff --git a/Android.mk b/Android.mk index 151621c..7bde511 100644 --- a/Android.mk +++ b/Android.mk @@ -150,6 +150,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IUpdateLock.aidl \ core/java/android/os/IUserManager.aidl \ core/java/android/os/IVibratorService.aidl \ + core/java/android/os/ILedService.aidl \ core/java/android/service/notification/INotificationListener.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ ~~~ 这个这个Android.mk位于frameworks/base/,编译后就会生成一个ILedService.java ### 添加service的实现cpp ~~~ #define LOG_TAG "LedService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> //#include <hardware_legacy/led.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> namespace android { #define ALOGI printf #define LED_NUM 3 int leds_fd[LED_NUM]; char path_buff[255]; static jint ledCtrl(JNIEnv *env, jobject clazz, jint which, jint status) { int ret = -1; if(status == 1) { ret = write(leds_fd[which], "255", 3); } else { ret = write(leds_fd[which], "0", 1); } if(ret < 0){ return -1; } ALOGI("Native ctrl fd = [%d]\n", which); return 0; } static jint ledOpen(JNIEnv *env, jobject clazz) { int i = 0; for(i=0; i<LED_NUM; i++){ sprintf(path_buff, "/sys/class/leds/led%d/brightness", i); printf("path:%s\n",path_buff); leds_fd[i] = open(path_buff, O_RDWR); if(leds_fd[i] < 0){ ALOGI("led%d: %s, open failed\n", i, path_buff); return -1; } else { ALOGI("led%d: %s, open success\n", i, path_buff); } } return 0; } static void ledClose(JNIEnv *env, jobject clazz) { int i = 0; for(i=0; i< LED_NUM; i++){ close(leds_fd[i]); } } static JNINativeMethod method_table[] = { { "native_ledCtrl", "(II)I", (void*)ledCtrl }, { "native_ledClose", "()V", (void*)ledClose }, { "native_ledOpen", "()I", (void*)ledOpen } }; int register_android_server_LedService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table)); } }; ~~~ 里面定义好了来调用这个native函数的java class名字为com_android_server_LedService: ~~~ jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table)); ~~~ 还需要添加到编译中: ~~~ $ git diff services/jni/Android.mk diff --git a/services/jni/Android.mk b/services/jni/Android.mk index b313d48..fb359cb 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -14,6 +14,7 @@ LOCAL_SRC_FILES:= \ com_android_server_UsbDeviceManager.cpp \ com_android_server_UsbHostManager.cpp \ com_android_server_VibratorService.cpp \ + com_android_server_LedService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ com_android_server_connectivity_Vpn.cpp \ onload.cpp ~~~ ### 添加LedService.java文件 前面有了native c/cpp的实现,接下来就需要用JNI来调用native方法了,因此需要添加LedService.java(frameworks/base/services/java/com/android/server/LedService.java)文件: ~~~ package com.android.server; import android.os.ILedService; /** * Created by hexiongjun on 12/9/15. * Function: * Call Native C function to control hardware */ public class LedService extends ILedService.Stub{ private static final String TAG = "LedService"; public int ledCtrl(int which, int status) throws android.os.RemoteException { return native_ledCtrl(which, status); } public void LedService(){ native_ledOpen(); } // Declare the function public native static int native_ledCtrl(int which, int status); public native static int native_ledOpen(); public native static void native_ledClose(); } ~~~ 内容很简单: - 声明了native函数 - 在构造函数中调用open打开设备 上层的Android.mk会自动将java文件添加到Android编译中,不需要自己添加。 ### 让SystemServer启动的时候加载service ~~~ $ git diff services/jni/onload.cpp diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index 423ebd1..83721fe 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -31,6 +31,8 @@ int register_android_server_SerialService(JNIEnv* env); int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); +// From com_android_server_LedService.cpp +int register_android_server_LedService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); @@ -60,6 +62,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_UsbDeviceManager(env); register_android_server_UsbHostManager(env); register_android_server_VibratorService(env); + register_android_server_LedService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); register_android_server_connectivity_Vpn(env); ~~~ 这个是因为SystemServer进程启动的时候会去调用LoadLibrary去加载各个库,这个加载的过程就在OnLoad.cpp中。 ### 添加Service到ServiceManager中 这个是在SystemServer中完成的: ~~~ $ git diff services/java/com/android/server/SystemServer.java diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9455017..1ccf63a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -129,6 +129,7 @@ class ServerThread extends Thread { PowerManagerService power = null; DisplayManagerService display = null; BatteryService battery = null; + LedService led = null; VibratorService vibrator = null; AlarmManagerService alarm = null; MountService mountService = null; @@ -288,6 +289,11 @@ class ServerThread extends Thread { battery = new BatteryService(context, lights); ServiceManager.addService("battery", battery); + //Add the led service to SystemServer, so others can use + Slog.i(TAG, "Led Service"); + led = new LedService(); + ServiceManager.addService("led", led); + Slog.i(TAG, "Vibrator Service"); vibrator = new VibratorService(context); ServiceManager.addService("vibrator", vibrator); ~~~ ### LED App中使用添加的Service 到了最后就可以使用这些服务了,但是要使用之前还需要添加包含了LedService的模块,这个模块其实是framework,但是因为我们是java,而framework属于dex格式,因此我们需要添加jar格式的包,这个包编译完成后,位于: > out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 因此在APP中添加此模块: ![](https://box.kancloud.cn/2016-05-05_572afc9e69af0.jpg) 并将此模块添加到app的依赖中: ![](https://box.kancloud.cn/2016-05-05_572afc9e88e99.jpg) 然后在代码中导入Interface与ServiceManager,并使用Service: ![](https://box.kancloud.cn/2016-05-05_572afc9ea1aa4.jpg) ## 这种添加Service到ServiceManager方法的问题 现在我们依然将硬件相关的操作放到了一个cpp中,而这个cpp会编译到系统中,因此如果对硬件的操作有变更,我们就需要修改这个文件,修改了这个文件,那么就需要将整个Android系统重新编译,因此图片中还有一个步骤④,这个就是将硬件相关的东西放在一个HAL层,这样子就避免了修改一个文件就需要编译整个系统,同时也可以不放出与硬件相关的源码而仅仅给出一个HAL相关的库(保密)。 ## 遇到的问题 ### multidex问题 因为包含了framework的classes.jar,而这个jar中有超过65K个的方法,因此就需要开启multidex。 ~~~ $ git diff app/build.gradle diff --git a/app/build.gradle b/app/build.gradle index 131397f..82a2a62 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,12 @@ android { targetSdkVersion 22 versionCode 1 versionName "1.0" + + //Enable multidex + multiDexEnabled true + } + dexOptions { + javaMaxHeapSize "4g" } sourceSets{ main { @@ -29,4 +35,6 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:design:22.2.1' + compile project(':classes') + compile 'com.android.support:multidex:1.0.0' } ~~~ 同时还需要更改xml文件: ~~~ $ git diff app/src/main/AndroidManifest.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a77e7a1..1504514 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.hexiongjun.led"> <application + android:name="android.support.multidex.MultiDexApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ~~~ 并对gradle resync。 ### jar不匹配的问题 如果重新编译了Android classes.jar但是在APP中依旧使用的是老的,那么会出现一些奇怪的问题: ![](https://box.kancloud.cn/2016-05-05_572afc9ec5da9.jpg) 此时需要先将老的移除掉然后重新添加,或者直接在app的workspace中替换新的。 ### javaHeap size的配置 ![](https://box.kancloud.cn/2016-05-05_572afc9eebcb2.jpg)