403-复习提纲
嵌入式安卓底层开发
第一章概述
第二章 源码开发环境搭建
-
Android 4.0开发环境搭建及源码编译
-
安装VMware Workstation pro -
安装ubuntu-12.04 -
gcc、g++降版本
-
把源码aosp源码拷贝到目录下 -
安装JDK1.6,修改 /etc/profile并且追加JDK的配置信息到文件的最后,通过 source /etc/profile使得更改生效 -
安装编译依赖工具包 -
解压源码、内核、sdk、eclipse压缩包 -
进入内核目录运行脚本build_kernel.sh进行编译linux内核 -
初始化编译环境 source build/envsetup.sh。 -
选择编译选项 lunch之后,选择目标编译项,应该选择ARM版本。 -
编译源码 make -j4,-j之后的数字是编译使用的线程数,一般最大可以是内核数的两倍。 -
源码编译完成,在out/target/product/generic/目录里出现三个文件:system.img、ramdisk.img、userdata.img说明编译成功。 -
创建模拟器 -
运行run_emulator.sh脚本 -
版本变量名由以下几个部分组成
- eng:工程版本
- user:最终用户版本
- userdebug:调试版本
- tests:测试版本
- 版本作用:
- 区分目标系统的所有的应用程序、库、测试程序等
- 根据不同的版本,系统会有不同的设置。
-
源码编译生成:
- system.img
- 文件系统,包含了android系统的应用程序,系统用到的各种库和资源,配置文件(etc目录),系统命令(bin,usr/bin,xbin)
- ramdisk.img
- userdata.img
- 内存磁盘映像,ramdisk.img是一个最小化的根文件系统,加载到内存中作为android的根文件系统
-
搭建android sdk开发环境
- 下载和安装eclipse,前面已经将其复制到目录下
- 安装ADT插件
- 下载配置Android SDK工具包,搭建Android SDK平台。
- 在Android源码目录下通过
android create avd -n avd -t 1 或者直接通过Android SDK Manager创建模拟器,定制Android模拟器。 - 运行模拟器,加载编译好的linux内核和android源码镜像文件,如果成功启动说明搭建完成,可以进行仿真。
-
编译命令
- make
- make ramdisk
- make snod
- mmm 模块目录(带有Android.mk文件)
- mkall
- 完全编译bootloader、kernel、android文件系统并且生成整体烧写镜像
- mksnod
第三章 系统的启动
-
Android系统的启动过程
- 开机上电后,启动BootLoader初始化堆栈和内存空间,为加载内核提供条件。
- BootLoader把Linux内核加载到内存,挂载根文件系统
- 启动init进程,init进程完成解析init.rc等初始化脚本文件,启动linux本地服务,启动Android本地守护进程。
- init进程解析init.rc启动zygote进程,zygote进程启动DVM,DVM中运行第一个java程序zygoteInit。zygote进程fork出System Server进程,在System Server进程启动和初始化时启动Android系统服务。
- Android系统服务启动完毕后,System Server进程通知开启Android桌面应用程序。
- 启动完成之后可以创建Android应用程序进程,每一个应用进程都是一个独立的DVM实例,其都是客户端通过Binder机制与System Server进程进行通信,然后System Server进程再通过socket与zygote进程通信,最后由zygote进程fork出来的。这些DVM实例通过共享zygote进程预加载的一部分资源来加快应用启动速度。
-
简答版本
- Android系统启动有4个阶段;
- 硬件BOOT、加载Linux内核并挂载Rootfs
- init进程启动及Native服务启动
- System Server及Android服务启动
- Home桌面启动
-
init进程启动
- 解析init.rc及init.{hardware}.rc初始化脚本文件
- 解释:init.rc只是语法文件,并不是程序,真正的入口时init.c
- 解释:/system/core/rootdit/正常启动使用的
- 解释:/bootab/recorecry/etc/刷机用到的
- 监听keychord组合按键事件
- 监听属性服务
- 属性服务好比window下的注册表,是系统定义的一系列键值对,这些属性影响系统的工作机制和功能
- 监听并处理子进程死亡事件
- 子进程的死亡由其父进程收尸,子进程的父进程提前结束就变成孤儿进程,由init接管
-
Android初始化语言四种类型的声明
-
Actions(行为)
- 其实就是一个Commands命令序列,每个Actions都有一个trigger(触发器),决定了触发行为的条件。
-
Commands(命令)
-
后面是一系列命令 -
常用的Commands -
命令 | 说明 |
---|
exec
[]*
| 创建和执行一个程序(
)。在程序完全执行前,init将会阻塞
| export | 在全局环境变量中设置环境变量为 | ifup | 启动网络接口 | hostname | 设置主机名 | chmod
| 更改文件访问权限 | chown
| 更改文件的所有者和组 | class_start | 启动所有指定服务类别下的未运行的服务 | class_stop | 停止指定服务类下的所有已运行的服务 | insmod
| 加载
中的模块
| mkdir
[mode] [owner] [group]
| 创建一个目录
,可以指定mode、owner、group。没有指定,默认的权限为755,属于root用户和root组
| mount
[ ]*
| 在目录挂载指定的设备 | setprop | 设置系统属性name为value | start/stop | 启动/停止指定服务 |
-
services(服务)
- 就是一个程序,一个本地守护进程,它被init进程启动,并在退出时可选择让其重启
-
options(选项)
-
Android中的本地守护进程
- Service 对应程序
ueventd /sbin/ueventd console /system/bin/sh adbd /sbin/adbd servicemanager /system/bin/servicemanager vold /system/bin/vold netd /system/bin/netd debugged /system/bin/debugged ril-daemon /system/bin/rild zygote /system/bin/app_process -Xzygote /system/bin --zygote–start-system-server media /system/bin/mediaserver - uevented进程
- 实现设备的管理
- 冷插拔:以预先定义的设备信息为基础,当init进程被启动时,统一创建设备节点文件
- 热插拔:系统运行中,有设备插入USB端口时,init进程就会接收到这一事件,动态的为新插入设备创建设备节点文件
- adbd进程
- servicemanager进程
- vold进程
- 负责系统的cdrom、usb大容量存储设备、mmc卡等拓展存储器的插拔等
- ril-daemon进程
- surfaceflinger进程
-
Home桌面的启动
- 显示Intent启动,在Intent对象中指定Intent对象的接收者,是点对点的启动方式
- 隐式Intent启动,类似于广播机制,在发送的Intent中通过Action和Category来匹配接收者
第四章 编译系统与系统定制
-
Android.mk必须完成的操作
- 指定当前模块的目录
- 清除所有的local_xx变量
- 指定源码文件
- 指定编译细节
- 制定目标模块名
- 指定目标模块类型
-
安卓主要预定义编译变量
-
编译变量 | 功能 |
---|
BUILD_SHARED_LIBRARY | 将模块编译成共享库 | BUILD_STATIC_LIBRARY | 将模块编译成静态库 | BUILD_EXECUTABLE | 将模块编译成可执行文件 | BUILD_JAVA_LIBRARY | 将模块编译成Java类库 | BUILD_PACKAGE | 将模块编译成Android应用程序包 |
-
主要编译变量
-
编译变量 | 功能 |
---|
local_path | 编译路径 | local_module | 编译模块名 | local_src_files | 编译源码列表 | local_shared_libraries | 使用的C/C++共享库列表 | local_static_libraries | 使用的C/C++静态库列表 | local_static_java_libraries | 使用的Java库列表 | local_cflags | 编译器参数 | local_c_includes | C/C++头文件路径 | local_package_name | Android应用程序名 | local_certificate | 签名认证 | local_java_libraries | Java库列表 |
-
编译可执行程序
-
编译静态库的模板
-
编译动态库的模板
-
编译一个简单的APK
-
编译Helloworld应用程序的步骤
-
打开eclipse开发环境,新建一个Android应用程序:HelloWorld
-
将新创建的HelloWorld工程复制到源码目录下的packages/apps目录下
-
编译HelloWorld工程的Android.mk文件,仿照Android自带应用程序的Android.mk文件,例如 Camera工程中的Android.mk,因此将其复制到HelloWorld中
-
编译Helloworld工程
-
定制开机界面
-
生成图片数组
- 使用Image2Lcd,它可以将一个图片转换成指定位色得C语言数组类型,我们只需要将这个图 片数据数组写入,即可在屏幕上显示这个图片了
-
设置logo.jpg输出文件属性
- 打开Image2Lcd选择实训xx目录中的test.bmp文件,设置test.bmp输出文件得属性。
- 由于Android模拟器得屏幕大小为宽320,高480,模拟器支持16位色,故设置为上述要求
-
保存图片数据为c语言头文件test.h -
将test.h图片数据文件拷贝到Linux内核framebuffer驱动目录下
-
修改framebuffer驱动文件goldfishfb.c
- 首先引入test.h,#include “test.h”
-
然后修改初始化工作,在prob函数返回之前,将图片数据写入到Framebuffer显存中。
-
重新编译Linux内核
-
启动Android模拟器运行实训结果 -
开机动画【蒙版图片】
-
使用Photoshop 等图像处理软件制作一张背景为黑色,中间镂空的 PNG:格式的图片,命名为:android-logo-mask.png -
将android-logo-mask.png 复制到 frameworks/base/core/res/assets/images/目录下替换Android 默认的图片,为了防止源码不编译图片资源,将图片时间戳更新一下。
-
重新编译Android的系统资源包framework-res.apk,以及运行make snod并且生成新的system.img -
source build/envsetup.sh
lunch 1
mmm frameworks/base/core/res/
make snod
-
运行脚本查看 -
开机动画【逐帧动画】
-
在/data/local/或/system/media/目录创建 bootanimation.zip 文件 -
bootanimation.zip文件打包前的结构如表所示。
-
如果bootanimation.zip放到/system/media/目录下,则重新编译生成system.img
第五章:JNI机制
-
使用JNI通常的场景
-
JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JVM中可能创建多个Java线程,每个线程对应一个JNIEnv结构,他们保存在线程本地存储中。因此,不同的线程的JNIEnv是不同,也不能相互共享调用。 -
JNI的数据类型和Java数据类型以及数据类型签名
-
JAVA类型 | JNI类型 | 签名 |
---|
boolean | jboolean | Z | byte | jbyte | B | char | jchar | C | short | jshort | S | int | jint | I | long | jlong | J | float | jfloat | F | double | jdouble | D | 引用类型 | jobject | L | void | | V | 数组类型 | jarray | [ | 二维数组类型 | | [[ |
-
JNI方法签名相关
-
JAVA方法 | JNI方法签名 |
---|
boolean isLedOn(); | ()Z或者(V)Z | double Div(int x,int y); | (II)D | int GetNameBy(int ModubleID); | (I)I | int GetArrayElementNumber(long[] num); | ([J)I | double Sub(double x,double y) | (DD)D | void setLedOn(int ledNo); | (I)V | String substr(String s,int idx,int count) | (Ljava/lang/String;II)Ljava/lang/String | char fun(int n,String s,int[] count) | (I;Ljava/lang/String;[I)C | boolean showMsg(android.View v, String msg); | (Landroid/lang/String;Ljava/lang/String)Z | int GetLedCount(); | ()I或者(V)I | String GetMoudleNameBy(int ModuleID); | (I)Ljava/lang/String | long GetArrayElement(long[] num , int index); | ([JI)J | float Add(float x , float y); | (FF)F | boolean isSameName(String mName); | (Ljava/lang/String)Z | void function(); | ()V或者(V)V | | |
-
JNI引用类型
-
JNI引用类型 | Java引用类型 |
---|
jobject | java.lang.Object类型 | jclass | java.lang.Class类型 | jstring | java.lang.String类型 | jarray | 数组类型 | jobjectArray | 对象数组类型 | jbooleanArray | 布尔数组类型 | jbyteArray | 字节数组类型 | jcharArray | 字符数组类型 | jshortArray | 短整型数组类型 | jintArray | 整形数组类型 | jlongArray | 长整型数组类型 | jfloatArray | 浮点数组类型 | jdoubleArray | 双精度浮点数组类型 | jthrowable | java.lang.Throwable类型 |
-
Java访问本地方法
-
JNI访问Java成员
-
在JNI中Java对象一般都是作为参数传递给本地方法的
-
class MyClass{
private int mNumber;
private static Sring mName;
public MyClass(){
}
public void printNum(){
System.out.println("Number:"+mNumber);
}
public static void printNm(){
System.out.println("Number"+mNumber);
}
}
class PassJavaObj{
static{
System.loadLibrary("native_method");
}
private native static void passObj(String str);
public static void main(){
passObj("Hello World");
}
}
-
本地代码 -
void Java_com_test_exam1_PassJavaObj_passObj
(JNIEnv *env,jclassthiz,jobject str)
{
...
}
-
env:当前Java代码的运行环境
thiz:表示调用当前本地方法的对象
str:就是我们传递过来的字符串
-
获得Java属性ID和方法ID
jfieldID 和 jmethodID 用来标识java对象的属性和方法。需要获得ID才能对属性和方法进行操作。而由于java中的重载机制,所以需 要使用签名 确定属性和方法。
-
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig);
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig);
-
jclass clazz:要取得成员对应的类
const char *name:代表要取得的方法名和属性名
const char *sig:代表要取得的方法或属性的签名
-
案例
-
class MyClass{
private int mNumber;
private static Sring mName="Michael";
public MyClass(){
mNumber=1000;
}
public void printNum(){
System.out.println("Number:"+mNumber);
}
public static void printNm(){
System.out.println("Number"+mNumber);
}
}
class NativeCallJava{
static{
System.loadLibrary("native_callback");
}
private native static void callNative(MyClass cls);
public static void main(){
callNative(new MyClass());
}
}
-
void Java_com_test_exam2_NativeCallJava_callNative(JNIEnv * env,jobject obj)
{
jclass myCls = env->GetObjectClass(obj);
jfieldID mNumFieldID=env->GetFieldID(myCls,"Number","I");
jfieldID mNameFieldID=env->GetStaticFieldID(myCls,"mName","java/lang/String");
jmethodID printNumMethodID =env->GetMethodID(myCls,"printNum"."(V)V");
jmethodID printNumMethodID =env->GetStaticMethodID(myCls,"printNm"."(V)V");
}
-
JNI操作Java属性和方法
-
j<类型> Get<类型>Field(jobject obj, jfieldID fieldID);
j<类型> Get Static<类型>Field(jobject obj, jfieldID fieldID);
void Set<类型>Field(jobject obj, jfieldID fieldID, j<类型> val);
void GetStatic<类型>Field(jobject obj, jfieldID fieldID, j<类型> val);
-
例如 -
jint GetIntField(jobejct obj,jfield fieldID);
void SetObjectField(jobjectobj,jfieldID fielded,jobejct val);
void SetStaticCharField(jclass clazz,jfieldID fieldID,jchar value)
-
例子 -
void Java_com_test_exam2_NativeCallJava_callNative(JNIEnv * env,jclassthiz,jobject obj)
{
jclass myCls = env->GetObjectClass(obj);
jfieldID mNumFieldID=env->GetFieldID(myCls,"Number","I");
jfieldID mNameFieldID=env->GetStaticFieldID(myCls,"mName","java/lang/String");
jint mNum=env->GetIntField(obj,mNumFieldID);
env->SetIntField(obj,mNumFieldID,mNum+100);
jstring mNum=(jstring)(env->GetStaticObjectField(myCls,mNameFieldID));
printf("%s\n",mNm);
jstring newStr=env->NewStringUTF("hello from Native");
env->SetStaticObejctField(myCls,mNumFieldID,newStr);
}
-
JNI调用Java中的方法
-
Call<类型>Method(jobject obj, jmethodID methodID, ...);
Call<类型>MethodV(jobject obj, jmethodID methodID, va_listargs);
Call<类型>MethodA(jobject obj, jmethodID methodID, const jvalue* args);
CallStatic<类型>Method(jobject obj, jmethodID methodID, ...);
CallStatic<类型>MethodV(jobject obj, jmethodID methodID, va_list args);
CallStatic<类型>MethodA(jobject obj, jmethodID methodID, const jvalue* args);
-
例子 -
void Java_com_test_exam2_NativeCallJava_callNative(JNIEnv * env,jclassthiz,jobject obj)
{
jclass myCls = env->GetObjectClass(obj);
jfieldID mNumFieldID=env->GetFieldID(myCls,"Number","I");
jfieldID mNameFieldID=env->GetStaticFieldID(myCls,"mName","java/lang/String");
jint mNum=env->GetIntField(obj,mNumFieldID);
env->SetIntField(obj,mNumFieldID,mNum+100);
jstring mNum=(jstring)(env->GetStaticObjectField(myCls,mNameFieldID));
printf("%s\n",mNm);
jstring newStr=env->NewStringUTF("hello from Native");
env->SetStaticObejctField(myCls,mNumFieldID,newStr);
jmethodID printNumMethodID=env->GetMethodID(myCls,"printNum","(V)V");
jmethodID printMmMethodID=env->GetStaticMethodID(myCls,"printNm","(V)V");
CallStaticVoidMethod(myCls,printNmMethodID)
}
-
在本地代码中创建Java对象
-
jobject NewObject(jclass clazz, jmethodID methodID, ...);
jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args);
jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue* args);
-
clazz:要创建的对象的类
jmethodID:创建对象对应的构造方法ID
参数列表:,表示是变长参数,以V结束的方法名表示向量表表示参数列表,以A结束的方法名表示以jvalue数组提供参数列表
-
在本地代码中创建Java对象 -
void Java_com_test_exam2_NativeCallJava_callNative(JNIEnv * env,jclassthiz,jobject obj)
{
jclass myCls=env->GetObjectClass(obj);
//当然可以像下面这样
//jclass myCls = env->FindClass("com/test/exam2/MyClass");
//取得MyClass的构造方法ID
jmethodID myClassMethodID=env->GetMethodID(myCls,"MyClass","(V)V");
//创建MyClass新对象
jobject newObj = NewObject(myCls,myClassMethodID);
}
-
本地代码操作Java String对象
-
jstring NewString(const jchar* unicode, jsize len);
jstring NewStringUTF(const char* utf);
const jchar* GetStringChars(jstring str, jboolean* isCopy);
const char* GetStringUTFChars(jstring str, jboolean* isCopy);
ReleaseStringChars(jstring jstr, const jchar* str);
ReleaseStringUTFChars(jstring jstr, const char* str);
-
本地代码操作Java数组
-
jsize GetArrayLength(jarray array);
jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init);
jobject GetObjectArrayElement(jobjectArray array, jsize index);
void SetObjectArrayElement(jobjectArray array, jsize index, jobject val);
j<类型>* Get<类型>ArrayElements(j<类型>Array array, jboolean* isCopy);
void Release<类型>ArrayElements(j<类型>Array array, j<类型>* elems, jint mode);
-
len:新创建对象数组长度
clazz:对象数组元素类型
init:对象数组元素的初始值
array:要操作的数组
index:要操作数组元素的下标索引
val:要设置的数组元素的值
-
例子 -
class ArrayTest{
static{
System.loadLibrary("native_array");
}
privateint [] arrays=new int[]{1,2,3,4,5};
publicnative void show();
publicstatic void main(String[] args){
new ArrayTest().show();
}
}
-
本地代码的编写: -
void Java_com_test_exam4_5_ArrayTest_ show(JNIEnv * env,jobject obj){
jfieldID id_arrsys=env->GetFieldID(env->GetObjectClass(obj),"arrays","[I");
jintArrayarr=(jintArray)(env->GetObjectField(obj,id_arrays));
jint * int_arr=env->GetIntArrayElements(arr,NULL);
jsizelen = env->GetArrayLength(arr);
for(int i=0;i<len;i++)
cout<<int_arr[i]<<endl;
env->ReleaseIntArrayElements(arr,int_arr,JNI_ABORT);
}
-
局部引用与全局引用 -
局部引用:
(1):只在上层Java调用本地代码的函数内有效,当本地代码返回时,局部引用自动回收。
(2)局部引用只在创建他们的线程里有效,本地代码不能将局部引用在多线程之间传递,使用时不能在一个线程调用另一个线程创建的局部引用,不能将一个局部引用保存在全局变量中在其他线程使用。(一定不能使用)
(3)默认传递给本地代码的引用都是局部引用,所有JNI函数的返回值都是局部引用。
全局引用:
(1)只有显式通知VM时,全局引用才会被回收,否则一直有效,Java的GC不会释放该引用的对象。
(2)全局引用可以跨多个线程使用,可以将全局引用保存在全局变量中或者使用另一个线程访问。(不过不建议保存在全局变量)
(3)全局引用在程序员手动释放之前一直有效,使用全局引用必须手动创建销毁,可以使用局部引用创建全局引用,不使用全局引 用时必须手动释放。
-
局部引用需要手动释放的情况
- 本地代码访问一个很大的java对象,使用完对象之后本地代码去执行耗时操作,这时本地代码还没有返回,需要手动释放引用对象
- 本地代码创建了大量的局部引用,导致JNI局部引用表溢出,这个时候需要及时的删除不被使用的局部引用
- 不返回的本地函数,如果函数中持续执行耗时操作不返回(比如无限循环),需要避免因无限创造局部引用导致内存泄漏手动释放局部引用。
-
全局引用
- 本地方法被多次调用时,可以使用一个全局引用跨越他们,一个全局引用可以跨越多个线程,并且在程序员手动释放之前,一直有效。
stringClass=env->NewGlobalRef(localRefCls);
env->DeleteLocalRef(localRefCls);
if(stringClass==NULL)
return NULL;
-
在Java环境中保存JNI对象
-
最好不要使用全局引用的方式在本地保存JNI的对象,一般可以在Java域中定义一个int类型的属性,在本地层将本地对象的指针转换为int类型,然后设置到Java域的属性中,然后需要的时候从Java域获取该int值,再转换为对应的指针类型,然后就可以通过指针获取值了。(不过需要注意的是需要创建的对象一定是在堆上创建的,要不然对象在函数结束之后就会被回收) -
static void java_native_setup(JNIEnv* env, jobject thiz)
{
JNIExcampleContext* context = new JNIExampleContext(env);
jclass clazz = env->GetObjectClass(thiz);
jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I");
env->SetIntField(thiz, fieldId, (int)context);
}
static void java_native_get(JNIEnv* env, jobject thiz)
{
jclass clazz = env->GetObjectClass(thiz);
jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I");
JNIExcampleContext* context = (JNIExcampleContext*)(env->GetIntField(thiz, fieldId));
}
-
本地方法的注册(重要)
-
需要将本地函数与Java中的native方法关联起来,这是通过一个结构体JNINativeMethod 实现的。这个结构体实现了本地函数和Java方法的映射。 typedef struct {
char* name;
char *signature;
void* fnPtr;
} JNINativeMethod;
-
JNI_OnLoad方法 -
当Java代码中通过System.loadLibrary方法加载本地库的时候,本地代码库被JVM加载,JVM自动调用本地代码中的JNI_OnLoad 函数。这个函数实现了本地方法注册的流程 -
jint JNI_OnLoad(JavaVM* vm, void* reserved);
-
vm:代表了JVM实例,其实是和JVM相关的一些操作函数指针
reserved:保留
-
JNI_OnLoad函数中需要做的工作:
(1)调用GetEnv函数,获取到JNIEnv实例,这是Java运行环境,里面封装了许多本地操作
(2)通过RegisterNatives函数注册本地方法
(3)返回JNI版本号
-
jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods);
jint UnregisterNatives(jclass clazz);
-
JNI 实例(非常重要)
-
在Linux操作系统中硬件通常有:open、read、write、close等相关操作接口,每个设备硬件还有一些自己的属性。 (1)用Java编写一个Screen “屏幕”设备类 (2)设备的初始化,设备打开,关闭,读/写都交给本地代码去实现。 a. 当写屏幕设备时,将写入内容存放在本地代码缓冲区中 b. 当读屏幕设备时,则将数据经过简单处理读取出来 例如:向Screen中写人a?z的小写字母,读出来变成A?Z的大写。 在Ubuntu系统中编写Java程序和本地C++代码,编写Makefile文件用来编译Java代码和本地代码库,最终正常运行Java与C++本地代码。
-
package com.test.practice4_8;
class Screen {
static {
System.loadLibrary("screen");
}
private String mDevName;
private int mDevNo;
private boolean isInit = false;
private int mWidth;
private int mHeight;
public Screen() {
mDevName = null;
mDevNo = 0;
}
public boolean isInit(){
return isInit;
}
public int getWidth(){
return mWidth;
}
public int getHeight(){
return mHeight;
}
public void printInfo(){
System.out.println("屏幕名:" + mDevName);
System.out.println("设备号:" + mDevNo);
System.out.println("屏幕宽度:" + mWidth);
System.out.println("屏幕高度:" + mHeight);
}
public native boolean open();
public native int read(byte[] data, int len);
public native int write(byte[] data, int len);
public native void close();
}
public class ScreenTest {
public static void main(String[] args) {
Screen dev = new Screen();
if (!dev.open()){
System.out.println("屏幕开启错误");
return;
}
dev.printInfo();
byte[] data = new byte[26];
for (int i = 0; i < data.length; i++){
data[i] = (byte)(97 + i);
}
System.out.println("向屏幕写入 a-z:");
dev.write(data, data.length);
byte[] buf = new byte[64];
int size = dev.read(buf, buf.length);
if (size < 0) {
System.out.println("从屏幕读取失败");
return;
}
System.out.println("从屏幕读取成功 A-Z");
System.out.print("大写字母:");
for (int i = 0; i < 26; i++) {
System.out.print((char) buf[i] + " ");
}
System.out.println();
dev.close();
}
}
-
本地实现代码 -
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#include <jni.h>
#include <jni_md.h>
typedef struct _screen {
jclass clazz;
jfieldID id_dev_name;
jfieldID id_dev_no;
jfieldID id_is_init;
jfieldID id_width;
jfieldID id_height;
} screen, *screen_ptr;
screen_ptr gfieldID;
char _data[64];
static int native_id_init(JNIEnv *env) {
gfieldID = (screen_ptr)malloc(sizeof(screen));
jclass _clazz = env->FindClass("com/test/practice4_8/Screen");
gfieldID->clazz = _clazz;
gfieldID->id_dev_name = env->GetFieldID(_clazz, "mDevName", "Ljava/lang/String;");
gfieldID->id_dev_no = env->GetFieldID(_clazz, "mDevNo", "I");
gfieldID->id_is_init = env->GetFieldID(_clazz, "isInit", "Z");
gfieldID->id_width = env->GetFieldID(_clazz, "mWidth", "I");
gfieldID->id_height = env->GetFieldID(_clazz, "mHeight", "I");
if(gfieldID == NULL){
return -1;
}
return 0;
}
static jboolean native_open(JNIEnv *env, jobject thiz){
if (native_id_init(env) != 0){
return JNI_FALSE;
}
jstring dev_nm = env->NewStringUTF("NAPO LCD Device");
if (dev_nm == NULL){
return JNI_FALSE;
}
env->SetObjectField(thiz, gfieldID->id_dev_name, dev_nm);
env->SetIntField(thiz, gfieldID->id_dev_no, 0x1024);
env->SetBooleanField(thiz, gfieldID->id_is_init, JNI_TRUE);
env->SetIntField(thiz, gfieldID->id_width, 2048);
env->SetIntField(thiz, gfieldID->id_height, 800);
return JNI_TRUE;
}
static jint native_read(JNIEnv *env, jobject thiz, jbyteArray arr, jint len){
if (len < 0) {
return len;
}
jbyte* byte_arr = env->GetByteArrayElements(arr, NULL);
int i = 0;
for ( ; i < len; i++) {
if (_data[i] - 32 < 0) break;
byte_arr[i] = _data[i] - 32;
}
env->ReleaseByteArrayElements(arr, byte_arr, 0);
return i;
}
static jint native_write(JNIEnv *env, jobject thiz, jbyteArray arr, jint len){
if (len > sizeof(_data) || len <= 0){
return len;
}
jbyte *byte_arr = env->GetByteArrayElements(arr, NULL);
int i = 0;
printf("小写字母:");
for (; i < len; i++) {
_data[i] = byte_arr[i];
printf("%c ", _data[i]);
}
printf("\n");
env->ReleaseByteArrayElements(arr, byte_arr, JNI_ABORT);
return i;
}
static void native_close(JNIEnv *env, jobject thiz){
env->SetBooleanField(thiz, gfieldID->id_is_init, JNI_FALSE);
free(gfieldID);
gfieldID = NULL;
}
static const JNINativeMethod gMethods[] = {
{(char*)"open", (char*)"()Z", (void*)native_open},
{(char*)"read", (char*)"([BI)I", (void*)native_read},
{(char*)"write", (char*)"([BI)I", (void*)native_write},
{(char*)"close", (char*)"()V", (void*)native_close},
};
static int registerMethods(JNIEnv* env){
static const char* className = "com/test/practice4_8/Screen";
jclass clazz = env->FindClass(className);
if(clazz == NULL){
printf("没发现这个类\n");
return -1;
}
if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) return -1;
return 0;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv *env = NULL;
jint result = -1;
if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK){
printf("获取ENV失败\n");
goto fail;
}
if(env == NULL){
goto fail;
}
if(registerMethods(env) != 0){
printf("注册方法失败\n");
goto fail;
}
result = JNI_VERSION_1_4;
fail:
return result;
}
-
# Makefile编写
libscreen.so: com_test_practice4_8_ScreenTest.cpp ScreenTest.class
g++ -I ~/aosp/jdk1.6.0_29/include -I ~/aosp/jdk1.6.0_29/include/linux/ $< -fPIC -shared -o $@
ScreenTest.class: ScreenTest.java
javac -d ./ $<
clean:
$(RM) ScreenTest.class Screen.class libscreen.so
-
# 启动脚本
#!/bin/bash
java -Djava.library.path='.' com/test/practice4_8/ScreenTest
第六章:Android的对象管理
Android的对象管理采用了智能指针,其中LightRefBase类是轻量级指针类,RefBase类是重量级指针类,RefBase 类中不仅仅定义了 mStrong 强引用计数,而且还有一个 mWeak 的弱引用计数,强引用计数主要被 sp 对象管理,弱引用计数主要被 wp 对象管理。
-
智能指针 -
智能指针
-
C++代码中创建对象的两种方式:创建栈对象 和 创建堆对象 -
class A {
public:
A();
~A();
private:
int mVar1;
int mVar2;
}
int main(int argc, char* argv){
A a();
A* a_ptr = new A();
}
-
在栈上创建对象是局部的,出了作用域就会被销毁,在堆上创建对象管理非常的复杂,必须要手动的进行销毁。实现了智能指针就如同java中的引用管理一样,能够通过引用计数智能的管理对象和引用,智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。智能指针的引用管理sp和wp使用了模板template实现了泛型,只要继承了智能指针的基类LightRefBase或RefBase就能被管理,体现了多态的思想。而且智能指针类中使用了重载运算符,重载了operator->和operator*来返回原始对象指针,能够让智能指针使用起来就像原始对象指针一样。
-
智能指针的概念
- 当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝;另一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享
-
轻量级指针
- 当使用智能指针时,只要继承LightRefBase类,那么子类对象就具有智能管理功能了。
- 类中定义一个mCount变量,初值为0;两个成员函数 incStrong和decStrong 来维护引用计数器的值。
- 需要注意的是,在desStrong函数中,如果当前引用计数值为1,那么当减1后就会变成0,于是就会delete这个对象。
-
轻量级指针实现有以下规定 -
(1)LightRefBase类的对象可以通过智能指针sp进行管理
(2)当使用智能指针sp指向、赋值、初始化LightRefBase对象时,该对象引用计数加1
(3)当sp指针使用完后,其指向的对象引用计数自动减1
(4)当LightRefBase对象的引用计数为0时,该对象会被删除
-
强引用和弱引用
- 强引用指针sp:表示一个对象的强引用关系,可以直接访问目标成员对象,并且直接管理着目标对象的销毁
- 弱引用指针wp:表示对一个对象的弱引用关系,不能像sp一样可以直接访问目标对象成员,如果要访问的话,必须调用promote函数由弱引用升级为强引用
- 强引用增加时,强弱引用计数都增加;强引用减少时,强弱引用计数都减少
- 弱引用增加时,只有弱引用计数增加;弱引用减少时,只有弱引用计数减少
第七章:Binder通信
Binder框架定义了四个角色,它们分别是:Server、Client、ServerManager (SMgr) 、Binder驱动 。其中前三者运行在用户空间,第四者运行于内核空间。
传统的Linux系统IPC通信机制:管道、消息队列、共享内存、套接字、信号量、信号
第八章:硬件抽象层
HAL:硬件设备抽象
- linux的设备驱动:
- Kernel中使用GPL协议开源一些非核心的接口访问代码
- 在Kernel上的应用层使用Apache协议,主要是硬件厂商不希望开源的逻辑代码,仅提供二进制代码
-
HAL存在的原因
-
HAL的两种框架形式
-
HAL使用方式
- HAL的代码被编译生成动态模块库,可以动态加载到内核中运行,Android应用程序和框架通过JNI加载并调用HAL module 库代码,在HAL module 库再去访问设备驱动。
-
HAL Stub 架构简称 321架构。因为其只需要三个结构体、两个常量、一个函数。
-
HAL Stub架构分析
-
三个结构体 -
struct hw_module_t{
uint32_t tag;
uint16_t version_major;
uint16_t version_minor;
const char* id;
const char* name;
const char* author;
struct hw_module_methods_t* methods;
void* dso;
uint32_t reserved[32];
};
struct hw_module_methods_t{
int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
};
struct hw_device_t{
uint32_t tag;
uint32_t version;
struct hw_module_t* module;
uint32_t reserved[12];
int (*close)(struct hw_device_t* device);
}
-
两个常量 -
#define HAL_MODULE_INFO_SYM HMI
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
-
一个函数 -
int hw_get_module(const char* id, const struct hw_module_t** module);
-
HAL Stub 注册
-
led_moudle_t -
继承led_moudle_t并且修改结构体 -
struct led_module_t {
struct hw_module_t common;
};
struct led_control_device_t {
struct hw_device_t common;
int (*getcount_led)(struct led_control_device_t *dev);
int (*set_on)(struct led_control_device_t *dev, int arg);
int (*set_off)(struct led_control_device_t *dev, int arg);
};
-
一定要注意的是common必须要做为第一个数据项。这样通过强制类型转换实现结构体的“多态”时才不会出错。
-
实现操作接口里对硬件操作的函数
-
static int led_device_close(struct hw_device_t* device)
{
struct led_control_context_t* ctx = (struct led_control_context_t*)device;
if (ctx) {
free(ctx);
}
close(fd);
return 0;
}
static int led_getcount(struct led_control_device_t *dev)
{
LOGI("led_getcount");
return 4;
}
static int led_set_on(struct led_control_device_t *dev, int arg)
{
LOGI("led_set_on");
ioctl(fd,LED_ON,arg);
return 0;
}
static int led_set_off(struct led_control_device_t *dev, int arg)
{
LOGI("led_set_off");
ioctl(fd,LED_OFF,arg);
return 0;
}
-
led_device_t和父结构体hw_device_t的关系
-
需要实现设备打开的函数,并将其封装到struct hw_module_methods_t结构体中去,将拓展的函数封装到struct led_control_device_t结构体中去
-
static int led_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
struct led_control_device_t* context;
context = (struct led_control_device_t*)malloc(sizeof(*context));
memset(context, 0, sizeof(*context));
context->common.tag= HARDWARE_DEVICE_TAG;
context->common.version = 0;
context->common.module = module;
context->common.close = led_device_close;
context->set_on = led_set_on;
context->set_off = led_set_off;
context->getcount_led = led_getcount;
*device = (struct hw_device_t*)context;
if((fd=open("/dev/led", O_RDWR))==-1)
{
LOGI("open error\n");
exit(1);
}
else LOGI("open ok\n");
return 0;
}
static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};
-
模块变量的名字必须为HMI 或者是HAL_MODULE_INFO_SYM (只有是这个名字才能从上层调用时使用hw_get_module函数找到它)
-
const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: "led HAL module",
author: "farsight",
methods: &led_module_methods,
},
};
-
用语言描述HAL的实现(简答题)
- 定义结构体hw_module_t 表示硬件、hw_module_methods_t 封装open函数、hw_device_t 表示硬件操作接口。
- hw_module_t中的methods指向hw_module_methods_t,是hw_module_methods_t类型的指针。
- hw_module_methods_t中的open是指向初始化函数的函数指针。
- 在初始化函数初始化结构hw_device_t,返回硬件操作接口。
- 定义名字为HMI的module对象,通过hw_get_module函数获取。
第九章:HAL硬件抽象层进阶 Sensor HAL 实例
-
Android系统内置支持的传感器
- 加速度传感器、磁力传感器、方向传感器、陀螺仪、环境光照传感器、压力传感器、温度传感器、距离传感器 等。
-
编写一个传感器的应用程序的步骤
- 通过调用 Context. getSystemService(SENSOR_ SERVICE)获得传感器服务,实现返回的是封装了 Sensorservice的 Sensor Manager对象。
- 调用 Sensor Manager. get Default Sensor( SensorTYPE_ ORIENTATION)来获得指定类型的传感器对象,方便获得传感器的数据。
- 通过 SensorManager registerlistener注册 SensorEvent listener监听器,监听获得的传感器对象,当传感器数据提交上来时,能被应用程序得到。
- 实现监听器里传感器上报数据的具体操作。
ff = led_set_off; context->getcount_led = led_getcount; device = (struct hw_device_t)context; if((fd=open(“/dev/led”, O_RDWR))==-1) { LOGI(“open error\n”); exit(1); } else LOGI(“open ok\n”); return 0; } // 将open函数封装到 struct hw_module_methods_t 结构体中去 static struct hw_module_methods_t led_module_methods = { open: led_device_open };
```
第九章:HAL硬件抽象层进阶 Sensor HAL 实例
-
Android系统内置支持的传感器
- 加速度传感器、磁力传感器、方向传感器、陀螺仪、环境光照传感器、压力传感器、温度传感器、距离传感器 等。
-
编写一个传感器的应用程序的步骤
- 通过调用 Context. getSystemService(SENSOR_ SERVICE)获得传感器服务,实现返回的是封装了 Sensorservice的 Sensor Manager对象。
- 调用 Sensor Manager. get Default Sensor( SensorTYPE_ ORIENTATION)来获得指定类型的传感器对象,方便获得传感器的数据。
- 通过 SensorManager registerlistener注册 SensorEvent listener监听器,监听获得的传感器对象,当传感器数据提交上来时,能被应用程序得到。
- 实现监听器里传感器上报数据的具体操作。
|