Android有四大组件
一、Activity
二、Service
三、Broadcast Receiver
四、Content Provider


Android硬件访问服务-Service_第1张图片

Service是Android中一个类,它是Android四大组件之一,使用Service可以在后台执行长时间的操作( perform long-running operations in the background ),Service并不与用户产生UI交互。其他的应用组件可以启动Service,即便用户切换了其他应用,启动的Service仍可在后台运行。一个组件可以与Service绑定并与之交互,甚至是跨进程通信(IPC)。例如,一个Service可以在后台执行网络请求、播放音乐、执行文件读写操作或者与 content provider交互 等。

由于上一章JNI直接访问硬件可能导致多个应用同时访问一个驱动,就可能导致驱动出现问题,所以我们可以只让一个应用程序来访问硬件,这个应用程序成为“SystemServer”,APP有应用请求统一发给它,由它统一管理所有的service。而我们这章的目的也是建立一个led service。

SystemServer使用java写的,访问硬件只能用C,所以中间也需要使用JNI。
SystemServer的源码在android目录下:
frameworks\base\services\java\com\android\server\SystemServer.java


这里先概述一下SystemServer的过程

  1. 通过LoadLibrary来加载C库,
  2. C库的JNI_Onload函数里面注册本地方法,分别调用各个硬件的函数来注册本地方法,比如LED、振动器、串口等等
  3. SystemServer:对每个硬件addService(Server里面有很多个Service,由Server提供Service),每个硬件都需构造Service(即注册本地方法)
  4. APP使用:
    首先获得服务:get Service
    然后使用服务:执行Service方法

SystemServer源码解析
文件:SystemServer.java
SystemServer在源码中是一个类,里面实现了很多类方法,以Vibrator这个service作为参考例子,模仿写一个led service,我们从main这个方法开始解析

  • Main()
    – SystemServer().run()
    -----System.loadLibrary(“android_servers”)—>初始化native service,即加载C库,对应文件是 onload.cpp,里面有JNI_OnLoad,用于注册本地方法
    --------Register_android_server_VibratorService(env)—>对应com_ android_server_VibratorService(对应文件名),里面负责实现native方法
    -----StartOtherServices()—>里面定义了各种service—>new VibratorService—>addService

想要APP能使用service需要注册添加各种service进service_manager.c,然后通过getService来获取接口,自己添加的驱动就需要在这里面addService,最上面那副图中的几个进程都会涉及到Binder driver,这个驱动程序并不是内核自带的,而是google公司对linux内核做的修改,添加的一个驱动程序,它可以实现更加高效的进程间通信。


接下来以代码的形式描述一下上面的整个过程,从APP—>server—>service—>JNI(其实应该还有个HAL,但这章主要描述service的建立过程,HAL放到下一章讲)。


整个过程文字描述如下

  1. 实现一个aidl(Android接口定义语言)文件,写出这个文件后用Android系统里面的编译命令让它帮我们自动生成那个ILedService.java(接口)文件,怎么写参考源码目录framework里面的振动器代码(IVibratorService.aidl,位于frameworks目录),这个aidl文件位于源码目录下的frameworks/base/core/java/android/os,然后修改它的Android.mk (位于frameworks/base/),vi Android.mk,全局搜索关键字“Virbrator”可以找到相应的aidl文件定义,添加上我们自己的Led(core/ java/android/os /ILedService.aidl)文件即可,修改完后在目录(frameworks/base/)下执行mmm .(mmm <目录名>)命令,可能会遇到报错(mmm: command not found),在android源码目录下执行“. build/envsetup.sh”即可解决,执行mmm .后系统会帮我们自动生成ILedService.java文件。编译成功后在out目录下搜索“find –name “ILedService.java””可以找到相应生成的java接口文件

  2. 实现LedService.java
    里面主要实现调用我们定义的本地方法然后修改SystemServer.java把这个service添加进去,之后APP就可以通过getService获得LedService从而操作LED了

  3. 实现com_android_server_LedService.cpp
    上面LedService.java的方法都是由这个文件来实现的,即JNI的相关代码,主要将数组里面的方法注册到LedService这个方法里面,并修改Onload.cpp将这个注册函数添加进去

  4. APP使用
    ILedService iLedService;
    iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
    iLedService.ledCtrl(0, 1);


整个过程代码描述如下

(1) AIDL

1. 把 ILedService.aidl 放入 frameworks/base/core/java/android/osaidl代码如下:
package android.os;/** {@hide} */interface ILedService{int ledCtrl(int which, int status);}
2. 修改 frameworks/base/Android.mk  添加一行         core/java/android/os/IVibratorService.aidl \+        core/java/android/os/ILedService.aidl \3. mmm frameworks/base(这里需要在源码根目录先运行. build/envsetup.sh)4. 它会生成: ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java5. ILedService.java里面定义了ledCtrl,我们需要去实现这个方法

(2) Server

实现:LedService.java

非常简单,就是提供native方法,这些方法最后需要我们写JNI代码去实现,代码如下:

package com.android.server;import android.os.ILedService;/* defind a LedService class */public class LedService extends ILedService.Stub {    private static final String TAG = "LedService";/* call native c function to access hardware */public int ledCtrl(int which, int status) throws android.os.RemoteException{return native_ledCtrl(which, status);/* this is a native method */}public LedService(){native_ledOpen();}public static native int native_ledCtrl(int which, int status);public static native int native_ledOpen();public static native void native_ledClose();}

把写好的service上传到服务器编译,上传到目录:
frameworks/base/services/core/java/com/android/server/LedService.java

由于添加了新的service,所以我们需要去修改下SystemServer.java,参考vibrator的这个service写即可,添加如下代码到startOtherServices:

Slog.i(TAG, "Led Service");ServiceManager.addService("led", new LedService());//The 'new' can call LedService Constructor

修改完后重新上传至:
frameworks/base/services/java/com/android/server/SystemServer.java

这里我们不需要去修改 frameworks/base/services/core/Android.mk
因为它的内容里已经把该目录下所有JAVA文件自动包含进去了,后面编译会自动重新加载这两个java文件:

LOCAL_SRC_FILES += \    $(call all-java-files-under,java)

(3) JNI

实现:com_android_server_LedService.cpp

上面LedService.java的方法都是由这个文件来实现的,即JNI的相关代码,主要将数组里面的方法注册到LedService这个方法里面,并修改Onload.cpp将这个注册函数添加进去,代码如下:

#define LOG_TAG "LedService"#include "jni.h"#include "JNIHelp.h"#include "android_runtime/AndroidRuntime.h"#include #include #include #include #include #include #include #include namespace android{static jint fd;jint ledOpen(JNIEnv *env, jobject cls){fd = open("/dev/leds_node", O_RDWR);//打开设备LED节点ALOGI("native ledOpen : %d", fd);if (fd >= 0)return 0;elsereturn -1;}   void ledClose(JNIEnv *env, jobject cls){  ALOGI("native ledClose ...");  close(fd);}jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status){int ret = ioctl(fd, which, status);//给设备节点传输控制信息ALOGI("native ledCtrl : %d, %d, %d", which, status, ret);return ret;}/* 定义JNI字段描述符 */static const JNINativeMethod methods[] = {//定义一个映射数组{"native_ledOpen", "()I", (void *)ledOpen},//对应c语言的ledOpen,这个函数没有参数,返回值是int类型{"native_ledClose", "()V", (void *)ledClose},//对应c语言的ledClose,这个函数没有参数,返回值是void类型{"native_ledCtrl", "(II)I", (void *)ledCtrl},//对应c语言的ledCtrl,这个函数有两个int参数,返回值是int类型};int register_android_server_LedService(JNIEnv *env){//注册方法return jniRegisterNativeMethods(env, "com/android/server/LedService",methods, NELEM(methods));}}

onload.cpp添加如下代码:

namespace android {........int register_android_server_LedService(JNIEnv* env);}extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){........register_android_server_LedService(env);}

把新文件上传到服务器, 所在目录:
frameworks/base/services/core/jni/onload.cpp
frameworks/base/services/core/jni/com_android_server_LedService.cpp

修改 frameworks/base/services/core/jni/Android.mk :

  • $(LOCAL_REL_DIR)/com_android_server_LedService.cpp

编译:
mmm frameworks/base/services
make snod


修改APP使用Service(此APP基于上一章“Android硬件访问服务-JNI”)

原来我们使用HardConrtol这个在JNI里面定义的类来访问,现在我们可以直接使用service来访问
首先在工程的MainActivity.java里面导入:

import android.os.ILedService;import android.os.ServiceManger;

在MainActivity这个类里面添加如下:

/* 定义一个Service成员 */private ILedService iLedService = null;

在OnCreate函数里面给service赋值,并且去掉之前的HardControl.ledopen()函数

/* 给service赋值*/iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));

Ctrl+R将所有的hardcontrol替换为ILedService


工程中需要包含什么:
在linux系统下Android源码目录使用指令:mmm frameworks/base show commands > log.txt 2>&1可以看到aidl文件修改后会涉及哪些文件,从这个文件里面我们可以看出最后会生成一个framework.jar文件,由于我们的ledservice是一个隐藏类(使用/** {@hide} */关键字描述的部分),所以我们是不是需要导入这个framework.jar呢?经搜索后发现我们应该导入classes.jar文件,因为framework.jar文件是dex格式文件,我们的android运行的程序并不是原原本本的java程序,它是把这些java目标文件转换为了dex格式的文件,dex文件是对java文件做了一些优化
jar文件目录:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

Android studio软件打开File->Project Structure ->左上角的’+’号添加->import .JAR->找到classed.jar文件导入->打开Modules下app的Dependencies->右边‘+’号添加classes即可,如下图
Android硬件访问服务-Service_第2张图片

在build.gradle(Module.app)里面添加如下几项:

android {.......defaultConfig {....        multiDexEnabled true    }    dexOptions{        javaMaxHeapSize "4g"    }.......}dependencies {....    compile project(':classes')}

在AndroidManifest.xml文件里面的“application”添加如下

    

解决以上编译错误的参考文章如下(大部分网址需要访问):

How do I build the Android SDK with hidden and internal APIs available?
http://stackoverflow.com/questions/7888191/how-do-i-build-the-android-sdk-with-hidden-and-internal-apis-available

Creating a module library and adding it to module dependencies
https://www.jetbrains.com/idea/help/configuring-module-dependencies-and-libraries.html

java.lang.OutOfMemoryError: GC overhead limit exceeded
Android Studio Google jar causing GC overhead limit exceeded error
http://stackoverflow.com/questions/25013638/android-studio-google-jar-causing-gc-overhead-limit-exceeded-error

Too many field references
Building Apps with Over 65K Methods
https://developer.android.com/tools/building/multidex.html

更多相关文章

  1. android使用ant编译找不到apkbuilder.jar的错误的解决方法
  2. 如何通过代码更改ANDROID的UI布局
  3. Android中/system/build.prop文件解读
  4. 2010.11.28(2)———android 展示网页 和 调用js代码
  5. Android AndroidManifest.xml文件的android:supportsRtl属性详解

随机推荐

  1. Android事件处理第一节(View对Touch事件的
  2. 精通Android
  3. Android菜鸟日记24-android小技巧
  4. Android(安卓)EditText/TextView使用Span
  5. Android开发艺术探索读书笔记(第二章)
  6. 利用Handler来更新android的UI(1)
  7. Android教程之Android自带的语音识别例子
  8. Android翻译:应用程序的生命周期 kill进程
  9. android电池(五):电池 充电IC(PM2301)驱动分析
  10. Androidc学习笔记二之四大布局及碎片理解