1.Android驱动框架
App1 App2 App3 App4
-------------------
硬件访问服务
-------------------
JNI
-------------------
C库
-------------------
Linux内核驱动
也就是说Android驱动 = Linux驱动 + 封装。
重点在与硬件访问服务,不同的硬件需要不同的硬件访问服务。
2.需要根据“韦东山Android系统视频使用手册20160303.pdf”的第六章安装Android Studio.
Me: 另外还需要计算机->属性->高级系统设置->环境变量 将jdk和jre的bin目录导入到path环境变量中。
3.app/src/main/res/layout/activity_main.xml文件对应于手机屏上看到的界面。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="www.100ask.net"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.176"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.241" />
a. TextView首字母大写就说明他是一个类对象,在其上面ctrl+h键可以查看这个类的继承关系。
发现其为view类的子类,双击TextView类就可以打开TextView.java源文件。
b. android:layout_width表示Text文本框的高度,赋值"wrap_content"表示其宽度/高度由内容决定。
若为"fill_parent"表示宽度要占满整个界面。
c.复选框是CheckBox
4.实验
(1)要求:
a. 点击ALL ON,变为ALL OFF,同时勾选4个LED
b. 点击ALL OFF,变为ALL ON,同时4个LED都取消勾选
c. 可以单独选择某一个LED。
(2)按钮按下时函数怎么写
双击.xml文件中的Button再按住Shift+F1会打开一个介绍Wegit中Button怎么使用的文档(但是3.31的AS中无效)
这个文档是sdk/docs/reference/android/widget下的Button.html,它里面介绍了如何如何在
点击Button的时候关联到函数。
里面有button.setOnClickListener()设置button的监听器,当buton被按下的时候这个函数就会被调用
button = () 鼠标定位在()内部,按ctrl+shift+空格 会自动帮我们强制类型转换 button = (Button)
button = (Button) findViewById(R.id.BUTTON); 双击BUTTON,然后按ctrl+B可以跳转到BUTTON定义的地方。
选中上面的BUTTON,然后Fn+Alt+F7跳转到引用这个Button的地方。
Build工程可以让AS根据xml生成一些源代码。
class MyButtonListener implements View.OnClickListener {
鼠标定位在这里,按ctrl+i, 双击弹出来的onClick()就表示要override
这个onClick(),代码会自动补全。
}
要想按下ALL ON时4个按键都被选中,首先要获取这四个按键的实例化对象。
MainActivity.java中要想根据id来找到控件就需要在activity_main.xml中定义这些控件的id.
android:onClick="onCheckboxClicked" 表明当CheckBox被点击的时候方法onCheckboxClicked就会被调用
双击选中一个函数,然后按ctrl+b就会跳转到函数定义的地方去。
4.activity_main.xml中添加一个Button
<Button Tab键,会补全基本内容
5.这里写的app程序可以在tiny4412开发板或在Android手机上运行,前提是手机打开了
开发者选项的USB调试。在tiny4412上运行的话使用mini USB 口语PC相连。此时再点击AS
的运行三角符号,就会弹出一个模拟器和一个正式的开发板。
===>此时会显示4412开发板的序列号,可以看是不是同一个开发板。
2.TODO:第一小节中提到的 Android_app视频_Mars 在哪???
JDK(Java 开发包), JRE(Java 运行环境)
disable.android.first.run=true
在JNI的C文件中使用这个函数可以吧log打印在串口上,printf好像不可以
__android_log_print()
TODO:
1.我的实验中模拟器(虚拟手机)上面什么都不显示,为什么 ?
2.查MainActivity.java中使用的toast类的成员函数的使用。
注App所在版本库:https://github.com/weidongshan/APP_0001_LEDDemo.git
=====================================================================
第1课第2节_让Android应用程序访问C库_P.mp4
-------------------------------------------
1.JNI编译生成的.so文件,根据报错信息,可以打包到java应用程序中,也可以
放在/vendor/lib或/system/lib下。
放到应用程序中的方法:
a. 在app/libs/下创建armeabi子目录,.so文件放在这里面。
b. 修改build.gradle文件,其实有2个build.grale文件,一个在AS工程根目录下,另一个在app目录下,
要修改的是app下面的build.gradle文件。
android {
...
sourceSets { //增加这一项
main {
jniLibs.srcDirs = ['libs'] //表示JNI库文件方法app/libs目录下
}
}
...
}
注意:
(1)若使用交叉编译器编译添加了JNI动态库文件就不能再使用模拟器运行了,因为模拟器是运行在x86上的。
(2)若在开发板上运行包找不到JNI动态库,排查:app\build\outputs\apk下面有生成.apk文件,可以使用2345好压打开它,看看app/libs里面是否有
动态库文件。对比名字是否写错了等。
(3)libhardcontrol.so依赖于libc.so.6这个库,但是找不到它的解决方法:
# ls /vendor/lib
# ls /system/lib/libc.so* ==>有libc.so但是没有libc.so.6
若重新编译开发板上的Android系统可以吧libc.so.6放进去,也可以指定就使用libc.so 修改编译成动态库的
编译选项,加上-nostdlib来指定链接哪个动态库,表示生成libhardcontrol.so的时候不会自动使用标准的libc库。
我们可以指定使用哪个libc库:
# cd work/android5.0.2/
# find -name libc.so -r ./ 发现有很多libc库,我们可以找一个arm平台的比较新的libc库,在-nostdlib后需要
加上这个库的路径名。
然后再从新编译这个app。
2.JNI文件中的JNI_OnLoad()中(*env)->FindClass(env, "com/thisway/hardlibrary/HardControl");
HardControl类位于包com.thisway.hardlibrary中,这里要给全路径,‘.’还要换成‘/’。
3.将JNI文件HardControl.c编译成C库:
arm-linux-gcc -fPIC -shared HardControl.c -o libHardControl.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
这样的话交叉编译工具链直接使用的就是x86 PC机的头文件了,这样合适吗? 会不会有些头文件有出入。
4.可以在手机/开发板上运行app程序,程序运行状态可以打印在AS中,可以用AS在线调试程序。 ##########
5.LED_Demo1\app\build\outputs\apk下面有生成.apk文件,可以使用2345好压打开它,看看app/libs里面是否有
动态库文件
6.此应用程序的入口是onCreate()方法,在里面调用JNI提供的open方法。
7.在JNI文件中加打印
虽然JNI文件是C编写的,但是也不能使用printf(),因为它打印的我们在AS里面看不见,这里想在AS的logcat窗
口打印这些信息需要使用Android提供的liblog库,用法:
#include <android/log.h>
__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");
eg:
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen ...");
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl: %d, %d", which, status);
以后写的C库比较复杂的话也可以使用这种方法加log帮助调试。
编译JNI成动态库的时候报找不到log.h,解决方法:
在Android源码包(work/android5.2.0/)下使用find查找log.h文件,选择一个最高版本的Android并且选择arm结构的log.h
即可。
app在开发板上执行的时候报找不到__android_log_print,解决方法:
在Android源码包(work/android5.2.0/)下使用find查找liblog*文件,选择一个arm平台的罪行的liblog.so文件,
在编译libhardcontrol.so的时候要加liblog.so的路径,链接到这个文件,然后在开发板上运行就没有这个错了。
TODO:编译成的动态库中使用的其它库的函数时,会把其它库的函数代码拷贝到此库中吗? 若不然为什么在运行时
没有再报找不到__android_log_print方法了!
========================================================
第1课第3节_Android程序操作LED_P.mp4
---------------------------------------
1.这一节主要就是写一个LED的驱动程序,19min处有使用工具烧录内核
2.驱动版本库:git clone https://github.com/weidongshan/DRV_0001_LEDs.git
========================================================
第1课第4.1节_Android硬件访问服务框架_P.mp4
------------------------------------------
1.硬件访问服务:只让一个程序访问硬件,叫做serverm,其它进程将访问请求发
给这个server.
流程:
a. Java通过JNI访问到C库,在C库中的JNI_OnLoad()中注册本地方法给Java使用,
在Java的静态代码块中调用loadlibrary()加载库。
b. SystemServer中将针对每个硬件的服务添加到系统中。
c. App先获得服务,getService,然后使用服务执行service方法。
注意:
1.server(服务器) service(服务), 服务器提供服务,首先SystemServer先对每一个硬件
构造service,然后再addService。
2.在SystemServer中加载C库来初始化本地服务,使用System.loadLibrary("android_servers"); libandroid_servers.so
对应的文件是Onload.cpp,它里面的JNI_OnLoad()分别调用各个硬件模块的函数注册
本地方法。
注意:JNI_OnLoad()里面注册函数的名字与文件对应是有规律的,例如 register_android_server_UsbDeviceManager,
其本地方法定义是在com_android_server_UsbDeviceManager.cpp中。它们称为JN文件,如果对硬件的操作比较简可以直接
在JNI文件中调用read/write/ioctl来访问硬件。但是如果对硬件的操作比较复杂,建议把它写在HAL文件中(也是在用户空间的),好处有两个:
(1)如果修改了硬件可以直接只修改编译HAL文件,然后把HAL文件对应的库放到系统中即可。但是如果在JNI文件中操作硬件的话,一旦修改
就需要重新编译烧写整个系统。
(2)有些硬件厂商不愿意公开其硬件操作文件,直接给出.so文件,此时直接使用JNI文件加载这个.so文件
就可以使用了。
3.硬件service和JNI文件和HAL文件还是两个不同的东西。SystemServer中startOtherServices()方法中
启动了很多硬件模块的service服务。
4.在SystemServer(.cpp)运行的进程中通过ServiceManager.addService()将一个服务告诉
系统,系统是Service_manager.c,它管理着系统中的所有服务,例如SerialService/VibratorService/LEDDemoService。
如果一个service想能被应用程序使用必须得把它注册进Service_manager.c中去。################
之后应用程序就可以向Service_manager.c查询获得service。
eg:
vibrator = new VibratorService(mSystemContext);
/*把这个vibrator服务告诉系统*/
ServiceManager.addService("vibrator", vibrator);
5.注意课件上的一个差别:当addService的时候是LedService.java,但是当应用程序getService
的时候却是ILedService.java,表明应用程序获得的是一个接口。应用程序通过ILedService这个接口将
服务请求发给LedService,然后由LedService实现对硬件的操作。
6.整个过程涉及三个进程
(1)SystemServer.java向service_manager.c注册添加各种service的进程
(2) 应用程序实际上就是一个客户端,它首先向service_manager.c查询
获得某一个服务,最后把这个服务请求发送给SystemServer.java
(3)
这些进程间的通信是通过内核的一个进程间通信机制binder机制实现的,比如SystemServer.java在
addService()的时候会调用到这个binder驱动。应用程序这个客户端在getService()的时候也会
涉及到binder驱动。应用程序这个客户端在向SystemServer.java发送服务请求的时候也会涉及
到binder驱动程序。
7.binder驱动并不是内核自带的,它是google公司对Linux内核做的一个修改,添加的一个驱动程序,它
可以实现更加高效的进程间通信。
8.硬件访问服务框架与实现
(1)实现JNI和HAL文件,JNI文件作用是注册JNI native方法和加载HAL库。HAL文件作用是调用open()/read()/write()实现硬件操作。
例如LEDDemo中实现JNI文件com_android_server_LedService.cpp,作用是注册JNI本地方法和加载hal_led.c编译出来的.so文件。
hal_led.c中实现对对硬件的操作。
(2) 修改onload.cpp文件,向Java层注册JNI文件中实现的native接口
例如调用com_android_server_LedService.cpp中实现的注册JNI native的方法。
(3) 修改SystemServer.java文件,new一个service对象然后添加此service。
例如new LedService, 然后add此service
(4)实现LedService.java 用于调用本地函数,提供硬件服务
(5)实现ILedService.java 给App向service_manager.c查询service使用