文档视界 最新最全的文档下载
当前位置:文档视界 › Android系统源代码情景分析2

Android系统源代码情景分析2

Android系统源代码情景分析2
Android系统源代码情景分析2

2 章

硬件抽象层

Android系统的硬件抽象层(Hardware Abstract Layer,HAL)运行在用户空间中,它向下屏蔽硬件驱动模块的实现细节,向上提供硬件访问服务。通过硬件抽象层,Android系统分两层来支持硬件设备,其中一层实现在用户空间中,另一层实现在内核空间中。传统的Linux系统把对硬件的支持完全实现在内核空间中,即把对硬件的支持完全实现在硬件驱动模块中。

Android系统为什么要把对硬件的支持划分为两层来实现呢?我们知道,一方面,Linux内核源代码是遵循GPL 1协议的,即如果我们在Android系统所使用的Linux内核中添加或者修改了代码,那么就必须将它们公开。因此,如果Android系统像其他的Linux系统一样,把对硬件的支持完全实现在硬件驱动模块中,那么就必须将这些硬件驱动模块源代码公开,这样就可能会损害移动设备厂商的利益,因为这相当于暴露了硬件的实现细节和参数。另一方面,Android系统源代码是遵循Apache License 2协议的,它允许移动设备厂商添加或者修改Android系统源代码,而又不必公开这些代码。因此,如果把对硬件的支持完全实现在Android系统的用户空间中,那么就可以隐藏硬件的实现细节和参数。然而,这是无法做到的,因为只有内核空间才有特权操作硬件设备。一个折中的解决方案便是将对硬件的支持分别实现在内核空间和用户空间中,其中,内核空间仍然是以硬件驱动模块的形式来支持,不过它只提供简单的硬件访问通道;而用户空间以硬件抽象层模块的形式来支持,它封装了硬件的实现

细节和参数。这样就可以保护移动设备厂商的利益了。

本章介绍Android系统的硬件抽象层的目的在于认识Android系统的体系结构,因为它的实现和使用依次涉及Android系统的硬件驱动模块、硬件抽象层、外部库和运行时库层、应用程序框架层和应用程序层等,如图2-1所示。图2-1 Android系统的体系结构

1 GNU General Public License,即GNU通用公共许可证,简称为GPL,是一个被广泛使用的自由软件许可证,具体可以参考https://www.docsj.com/doc/cf7084676.html,/copyleft/gpl.html。

2 Apache Licence是一种对商业应用友好的许可,由非盈利开源组织Apache创建,具体可以参考https://www.docsj.com/doc/cf7084676.html,/licenses/LICENSE-2.0.html。

接下来,我们就以硬件抽象层为中心,首先在Android系统的内核空间中为一个硬件开发驱动程序,接着在用户空间中为该硬件添加一个硬件抽象层模块,并且在应用程序框架层中添加一个硬件访问服务,最后开发一个应用程序来访问该硬件服务。这样我们就可以从下到上来认识Android系统的体系结构。

2.1 开发Android硬件驱动程序

为了方便描述,我们将为一个虚拟的字符硬件设备开发驱动程序。这个虚拟的字符硬件设备只有一个寄存器,它的大小为4字节,可读可写。由于这个字符设备是虚拟的,且只有一个寄存器,因此,我们将它称为“fake register”,并且将对应的驱动程序命名为freg。

在Android系统中开发硬件驱动程序的方法与一般Linux系统是一样的,因此,本节假设读者已经对Linux设备驱动有一定的了解,具体可以参考在前面1.1小节中介绍的Linux Device Drivers一书。接下来,我们首先实现驱动程序freg,然后介绍它的编译方法,最后验证它的功能正确性。

2.1.1 实现内核驱动程序模块

驱动程序freg的目录结构如下:

~/Android/kernel/gold fi sh

----drivers

----freg

----freg.h

----freg.c

----Kcon fi g

----Make fi le

它由四个文件组成,其中freg.h和freg.c是源代码文件,Kconfig是编译选项配置文件,Makefile是编译脚本文件。下面我们就分别介绍这四个文件的实现。

freg.h

01 #ifndef _FAKE_REG_H_

02 #de fi ne _FAKE_REG_H_

03

04 #include

05 #include

06

07 #de fi ne FREG_DEVICE_NODE_NAME "freg"

08 #de fi ne FREG_DEVICE_FILE_NAME "freg"

09 #de fi ne FREG_DEVICE_PROC_NAME "freg"

10 #de fi ne FREG_DEVICE_CLASS_NAME "freg"

11

12 struct fake_reg_dev {

13 int val;

14 struct semaphore sem;

15 struct cdev dev;

16 };

17

18 #endif

这个文件定义了四个字符串常量,分别用来描述虚拟硬件设备freg在设备文件系统中的名称。此外,此文件还定义了一个结构体fake_reg_dev,用来描述虚拟硬件设备freg。在结构体fake_reg_dev中,成员变量val用来描述一个虚拟寄存器,它的类型为int;成员变量sem是一个信号量,用来同步访问虚

拟寄存器val;成员变量dev是一个标准的Linux字符设备结构体变量,用来标志该虚拟硬件设备freg的类型为字符设备。

freg.c

这个文件是驱动程序freg的实现文件,它向用户空间提供了三个接口来访问虚拟硬件设备freg中的寄存器val。第一个是proc文件系统接口,第二个是传统的设备文件系统接口,第三个是devfs文件系统接口。下面我们就分段介绍该驱动程序的实现。

文件开头包含了必要的头文件,以及定义了一些相关的变量和函数原型,它们的含义可以参考代码中的注释。

001 #include

002 #include

003 #include

004 #include

005 #include

006 #include

007 #include

008

009 #include "freg.h"

010

011 /*主设备号和从设备号变量*/

012 static int freg_major = 0;

013 static int freg_minor = 0;

014

015 /*设备类别和设备变量*/

016 static struct class* freg_class = NULL;

017 static struct fake_reg_dev* freg_dev = NULL;

018

019 /*传统的设备文件操作方法*/

020 static int freg_open(struct inode* inode, struct fi le* fi lp);

021 static int freg_release(struct inode* inode, struct fi le* fi lp);

022 static ssize_t freg_read(struct fi le* fi lp, char __user *buf, size_t count, loff_t* f_pos); 023 static ssize_t freg_write(struct fi le* fi lp, const char __user *buf, size_t count, loff_t* f_pos); 024

025 /*传统的设备文件操作方法表*/

026 static struct fi le_operations freg_fops = {

027 .owner = THIS_MODULE,

028 .open = freg_open,

029 .release = freg_release,

030 .read = freg_read,

031 .write = freg_write,

032 };

033

034 /*devfs文件系统的设备属性操作方法*/

035 static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf); 036 static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);

037

038 /*devfs文件系统的设备属性*/

039 static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, freg_val_show, freg_val_store);

接下来,定义用来访问虚拟硬件设备freg的传统设备文件系统接口,主要是实现freg_open、freg_ release、freg_read和freg_write四个函数,其中,前面两个函数用来打开和关闭虚拟硬件设备freg,而后面两个函数用来读取和写入虚拟硬件设备freg中的寄存器val。这些函数的实现细节可以参考代码中的注释,如下所示。

040 /*打开设备方法*/

041 static int freg_open(struct inode* inode, struct fi le* fi lp) {

042 struct fake_reg_dev* dev;

043

044 /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时可以直接拿来用*/

045 dev = container_of(inode->i_cdev, struct fake_reg_dev, dev);

046 fi lp->private_data = dev;

047

048 return 0;

049 }

050

051 /*设备文件释放时调用,空实现*/

052 static int freg_release(struct inode* inode, struct fi le* fi lp) {

053 return 0;

054 }

055

056 /*读取设备的寄存器val的值*/

057 static ssize_t freg_read(struct fi le* fi lp, char __user *buf, size_t count, loff_t* f_pos) { 058 ssize_t err = 0;

059 struct fake_reg_dev* dev = fi lp->private_data;

060

061 /*同步访问*/

062 if(down_interruptible(&(dev->sem))) {

063 return -ERESTARTSYS;

064 }

065

066 if(count < sizeof(dev->val)) {

067 goto out;

068 }

069

070 /*将寄存器val的值拷贝到用户提供的缓冲区中*/

071 if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {

072 err = -EFAULT;

073 goto out;

074 }

075

076 err = sizeof(dev->val);

077

078 out:

079 up(&(dev->sem));

080 return err;

081 }

082

083 /*写设备的寄存器val的值*/

084 static ssize_t freg_write(struct fi le* fi lp, const char __user *buf, size_t count, loff_t* f_pos) { 085 struct fake_reg_dev* dev = fi lp->private_data;

086 ssize_t err = 0;

087

088 /*同步访问*/

089 if(down_interruptible(&(dev->sem))) {

090 return -ERESTARTSYS;

091 }

092

093 if(count != sizeof(dev->val)) {

094 goto out;

095 }

096

097 /*将用户提供的缓冲区的值写到设备寄存器中*/

098 if(copy_from_user(&(dev->val), buf, count)) {

099 err = -EFAULT;

100 goto out;

101 }

102

103 err = sizeof(dev->val);

104

105 out:

106 up(&(dev->sem));

107 return err;

108 }

接下来,我们继续定义用来访问虚拟硬件设备freg的devfs文件系统接口。这种硬件访问接口将虚拟硬件设备freg的寄存器val当作设备的一个属性,通过读写这个属性就可以达到访问设备的目的,这是通过调用freg_val_show和freg_val_store这两个函数来实现的。为了方便后面编写proc文件系统接口来访问虚拟硬件设备freg,我们定义了两个内部使用的函数__freg_get_val和__freg_set_val,它们分别用来读写虚拟硬件设备freg的寄存器val。它们的实现如下所示。

109 /*将寄存器val的值读取到缓冲区buf中,内部使用*/

110 static ssize_t __freg_get_val(struct fake_reg_dev* dev, char* buf) {

111 int val = 0;

112

113 /*同步访问*/

114 if(down_interruptible(&(dev->sem))) {

115 return -ERESTARTSYS;

116 }

117

118 val = dev->val;

119 up(&(dev->sem));

120

121 return snprintf(buf, PAGE_SIZE, "%d\n", val);

122 }

123

124 /*把缓冲区buf的值写到设备寄存器val中,内部使用*/

125 static ssize_t __freg_set_val(struct fake_reg_dev* dev, const char* buf, size_t count) { 126 int val = 0;

127

128 /*将字符串转换成数字*/

129 val = simple_strtol(buf, NULL, 10);

130

131 /*同步访问*/

132 if(down_interruptible(&(dev->sem))) {

133 return -ERESTARTSYS;

134 }

135

136 dev->val = val;

137 up(&(dev->sem));

138

139 return count;

140 }

141

142 /*读设备属性val的值*/

143 static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf) { 144 struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);

145

146 return __freg_get_val(hdev, buf);

147 }

148

149 /*写设备属性val的值*/

150 static ssize_t f re g_va l_store(struct device* dev, str uct d evi ce_at t r i b u te* at t r,c o n s t ch a r*b u f, si z e_t c ou n t){ 151 struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);

152

153 return __freg_set_val(hdev, buf, count);

154 }

接下来,我们继续定义用来访问虚拟硬件设备freg的proc文件系统接口,主要是实现freg_proc_ read和freg_proc_write这两个函数。同时,我们还定义了用来在proc文件系统中创建和删除/proc/freg文件的函数freg_create_proc和freg_remove_proc。它们的实现如下所示。

155 /*读取设备寄存器val的值,保存到page缓冲区中*/

156 static ssize_t freg_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) { 157 if(off > 0) {

158 *eof = 1;

159 return 0;

160 }

161

162 return __freg_get_val(freg_dev, page);

163 }

164

165 /*把缓冲区的值buff保存到设备寄存器val中*/

166 static ssize_t freg_proc_write(struct fi le* fi lp, const char __user *buff, unsigned long len, void* data) { 167 int err = 0;

168 char* page = NULL;

169

170 if(len > PAGE_SIZE) {

171 printk(KERN_ALERT"The buff is too large: %lu.\n", len);

172 return -EFAULT;

173 }

174

175 page = (char*)__get_free_page(GFP_KERNEL);

176 if(!page) {

177 printk(KERN_ALERT"Failed to alloc page.\n");

178 return -ENOMEM;

179 }

180

181 /*先把用户提供的缓冲区的值拷贝到内核缓冲区中*/

182 if(copy_from_user(page, buff, len)) {

183 printk(KERN_ALERT"Failed to copy buff from user.\n");

184 err = -EFAULT;

185 goto out;

186 }

187

188 err = __freg_set_val(freg_dev, page, len);

189

190 out:

191 free_page((unsigned long)page);

192 return err;

193 }

194

195 /*创建/proc/freg文件*/

196 static void freg_create_proc(void) {

197 struct proc_dir_entry* entry;

198

199 entry = create_proc_entry(FREG_DEVICE_PROC_NAME, 0, NULL);

200 if(entry) {

201 entry->owner = THIS_MODULE;

202 entry->read_proc = freg_proc_read;

203 entry->write_proc = freg_proc_write;

204 }

205 }

206

207 /*删除/proc/freg文件*/

208 static void freg_remove_proc(void) {

209 remove_proc_entry(FREG_DEVICE_PROC_NAME, NULL);

210 }

最后,我们定义驱动程序freg的模块加载与卸载函数freg_init和freg_exit。函数freg_init主要用来注册和初始化虚拟硬件设备freg,而函数freg_exit用来反注册和释放虚拟硬件设备freg。它们的实现如下所示。

211 /*初始化设备*/

212 static int __freg_setup_dev(struct fake_reg_dev* dev) {

213 int err;

214 dev_t devno = MKDEV(freg_major, freg_minor);

215

216 memset(dev, 0, sizeof(struct fake_reg_dev));

217

218 /*初始化字符设备*/

219 cdev_init(&(dev->dev), &freg_fops);

220 dev->dev.owner = THIS_MODULE;

221 dev->dev.ops = &freg_fops;

222

223 /*注册字符设备*/

224 err = cdev_add(&(dev->dev),devno, 1);

225 if(err) {

226 return err;

227 }

228

229 /*初始化信号量和寄存器val的值*/

230 init_MUTEX(&(dev->sem));

231 dev->val = 0;

232

233 return 0;

234 }

235

236 /*模块加载方法*/

237 static int __init freg_init(void) {

238 int err = -1;

239 dev_t dev = 0;

240 struct device* temp = NULL;

241

242 printk(KERN_ALERT"Initializing freg device.\n");

243

244 /*动态分配主设备号和从设备号*/

245 err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);

246 if(err < 0) {

247 printk(KERN_ALERT"Failed to alloc char dev region.\n"); 248 goto fail;

249 }

250

251 freg_major = MAJOR(dev);

252 freg_minor = MINOR(dev);

253

254 /*分配freg设备结构体*/

255 freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);

256 if(!freg_dev) {

257 err = -ENOMEM;

258 printk(KERN_ALERT"Failed to alloc freg device.\n");

259 goto unregister;

260 }

261

262 /*初始化设备*/

263 err = __freg_setup_dev(freg_dev);

264 if(err) {

265 printk(KERN_ALERT"Failed to setup freg device: %d.\n", err); 266 goto cleanup;

267 }

268

269 /*在/sys/class/目录下创建设备类别目录freg*/

270 freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME); 271 if(IS_ERR(freg_class)) {

272 err = PTR_ERR(freg_class);

273 printk(KERN_ALERT"Failed to create freg device class.\n");

274 goto destroy_cdev;

275 }

276

277 /*在/dev/目录和/sys/class/freg目录下分别创建设备文件freg*/

278 temp = device_create(freg_class, NULL, dev, "%s", FREG_DEVICE_FILE_NAME); 279 if(IS_ERR(temp)) {

280 err = PTR_ERR(temp);

281 printk(KERN_ALERT"Failed to create freg device.\n");

282 goto destroy_class;

283 }

284

285 /*在/sys/class/freg/freg目录下创建属性文件val*/

286 err = device_create_fi le(temp, &dev_attr_val);

287 if(err < 0) {

288 printk(KERN_ALERT"Failed to create attribute val of freg device.\n"); 289 goto destroy_device;

290 }

291

292 dev_set_drvdata(temp, freg_dev);

293

294 /*创建/proc/freg文件*/

295 freg_create_proc();

296

297 printk(KERN_ALERT"Succedded to initialize freg device.\n");

298

299 return 0;

300

301 destroy_device:

302 device_destroy(freg_class, dev);

303 destroy_class:

304 class_destroy(freg_class);

305 destroy_cdev:

306 cdev_del(&(freg_dev->dev));

307 cleanup:

308 kfree(freg_dev);

309 unregister:

310 unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1);

311 fail:

312 return err;

313 }

314

315 /*模块卸载方法*/

316 static void __exit freg_exit(void) {

317 dev_t devno = MKDEV(freg_major, freg_minor);

318

319 printk(KERN_ALERT"Destroy freg device.\n");

320

321 /*删除/proc/freg文件*/

322 freg_remove_proc();

323

324 /*销毁设备类别和设备*/

325 if(freg_class) {

326 device_destroy(freg_class, MKDEV(freg_major, freg_minor));

327 class_destroy(freg_class);

328 }

329

330 /*删除字符设备和释放设备内存*/

331 if(freg_dev) {

332 cdev_del(&(freg_dev->dev));

333 kfree(freg_dev);

334 }

335

336 /*释放设备号资源*/

337 unregister_chrdev_region(devno, 1);

338 }

339

340 MODULE_LICENSE("GPL");

341 MODULE_DESCRIPTION("Fake Register Driver");

342

343 module_init(freg_init);

344 module_exit(freg_exit);

驱动程序freg的源文件准备好之后,接下来就要为它编写编译选项配置文件和编译脚本文件了。Kcon?g

1 con fi g FREG

2 tristate "Fake Register Driver"

3 default n

4 help

5 This is the freg driver for android system.

这个文件定义了驱动程序freg的编译选项。在编译驱动程序freg之前,我们可以通过执行make menuconfig命令来设置这些编译选项,以便可以指定驱动程序freg的编译方式。从这个配置文件就可以看出,驱动程序freg可以以三种方式来编译。第一种方式是直接内建在内核中;第二种方式是编译成内核模块;第三种方式是不编译到内核中。默认的编译方式为n,即不编译到内核中,因此,在编译驱动程序freg之前,我们需要执行make menuconfig命令来修改它的编译选项,以便可以将驱动程序freg 内建到内核中或者以模块的方式来编译。

Make?le

1 obj-$(CONFIG_FREG) += freg.o

这是驱动程序freg的编译脚本文件,其中,$(CONFIG_FREG)是一个变量,它的值与驱动程序freg 的编译选项有关。如果选择将驱动程序freg内建到内核中,那么变量$(CONFIG_FREG)的值为y;如果选择以模块的方式来编译驱动程序freg,那么变量$(CONFIG_FREG)的值为m;如果变量$(CONFIG_ FREG)的值既不为y,也不为m,那么驱动程序freg就不会被编译。

至此,我们就为虚拟硬件设备freg开发了一个驱动程序。接下来,我们还需要修改内核中的Kconfig 和Makefile文件来支持驱动程序freg的编译。

2.1.2 修改内核Kconfig文件

虽然我们在2.1.1小节中为驱动程序freg编写了一个Kconfig文件,但是在默认情况下,在执行make menuconfig命令配置内核编译选项时,编译系统是无法找到这个Kconfig文件的。这时候,我们需要修改内核的根Kconfig文件,使得编译系统能够找到驱动程序freg的Kconfig文件。

当执行make menuconfig命令时,编译系统会读取arch/$(ARCH)目录下的Kconfig文件,其中,$(ARCH)指向编译的目标CPU体系架构。在前面的1.4.2小节中,我们将$(ARCH)的值设置为arm,因此,就需要修改arch/arm目录下的Kconfig文件,使得编译系统可以找到驱动程序freg的Kconfig文件。打开arch/arm/Kconfig文件,找到以下两行内容:

menu "Device Drivers"

……

endmenu

在这两行内容之间添加下面一行内容,将驱动程序freg的Kconfig文件包含进来。

menu "Device Drivers"

source "drivers/freg/Kcon fi g"

……

endmenu

这样,当我们执行make menuconfig命令来配置内核编译选项时,编译系统就可以找到驱动程序freg中的Kconfig文件了,这时候,我们就可以配置驱动程序freg的编译方式了。

一般来说,各个CPU体系架构目录下的Kconfig文件都会通过source "drivers/Kconfig"命令把drivers 目录下的Kconfig文件包含进去。例如,x86体系架构下的Kconfig文件有下面一行内容:

……

source "drivers/Kcon fi g"

……

这意味着当我们在内核中新增了一个驱动程序时,只需要将它的Kconfig文件包含drivers目录下的Kconfig文件就可以了。但是arm体系架构比较特殊,它没有将drivers目录下的Kconfig文件包含到自身的Kconfig文件中,而是将drivers/Kconfig文件的内容原封不动地拷贝到它的Kconfig文件中。因此,考虑到兼容其他体系架构,我们最好也以同样的方式来修改drivers/Kconfig文件,即在里面添加一行:source "drivers/freg/Kconfig"。

2.1.3 修改内核Makefile文件

与Kconfig文件相似,虽然我们在2.1.1小节中为驱动程序freg编写了一个编译脚本文件Makefile,但是在默认情况下,在执行make命令编译内核时,编译系统是无法找到这个Makefile文件的。这时候,我们需要修改drivers目录下的Makefile文件,使得编译系统能够找到驱动程序freg的Makefile文件。打开drivers/Makefile文件,在里面添加以下一行内容:

……

obj-$(CONFIG_FREG)+= freg/

这样,当我们执行make命令来编译内核时,编译系统就会对驱动程序freg进行编译。

2.1.4 编译内核驱动程序模块

前面提到,在编译驱动程序freg之前,我们需要执行make menuconfig命令来配置它的编译方式。USER@MACHINE:~/Android/kernel/gold fi sh$ make menucon fi g

在弹出来的第一个配置界面中用上下箭头键选择“Device Drivers”项,按Enter键;接着在弹出来的第二个配置界面中继续用上下箭头键选择“Fake Register Driver”项,按Y键或者M键,就可以看到选项前面方括号中的字符变成“*”或者“M”符号,它们分别表示将驱动程序freg编译到内核中或者以模块的方式来编译。

││ [*] Fake Register Driver ││

……

注意

如果我们要以模块的方式来编译驱动程序freg,那么就必须先在第一个配置界面中选择“Enable loadable module support”选项,并且按Y键将它的值设置为true,即使得内核可以支持动态加载模块,这样才能在第二个配置界面中按M键来配置“Fake Register Driver”选项。同样,如果要使得内核支持动态卸载模块,那么就要在第一个配置界面中选择“Enable loadable module support”选项中的子选项“Module unloading”,并且按Y键将它的值设置为true。

配置完成后,保存编译配置选项,退出make menuconfig命令,然后就可以执行make命令来编译驱动程序freg了。

USER@MACHINE:~/Android/kernel/gold fi sh$ make

当看到下面的输出时,就说明驱动程序freg编译成功了。

OBJCOPY arch/arm/boot/zImage

Kernel: arch/arm/boot/zImage is ready

编译得到的内核镜像文件zImage保存在arch/arm/boot目录下,我们可以使用它来启动Android模拟器。

2.1.5 验证内核驱动程序模块

编译了驱动程序freg之后,我们就可以通过proc文件系统和devfs文件系统来验证它的功能是否正确。我们首先使用前面在2.1.4小节中得到的内核镜像文件zImage来启动Android模拟器,然后用adb工具连接上它,最后就可以使用cat和echo命令来读写/proc/freg文件或者/sys/class/freg/freg/val文件的内容了,即读写虚拟硬件设备freg的寄存器val的内容。如果读出来的内容与上次写入的内容相同,就说明我们为虚拟硬件设备freg所编写的驱动程序freg是正确的。

在读写虚拟硬件设备freg的寄存器val的内容之前,我们需要检查设备上的/dev目录下是否存在一个设备文件freg。如果存在,就说明驱动程序freg成功地将虚拟硬件设备freg注册到设备文件系统中了。

USER@MACHINE: ~/Android$ emulator -kernel kernel/gold fi sh/arch/arm/boot/zImage &

USER@MACHINE: ~/Android$ adb shell

root@android:/ # cd dev

root@android:/dev # ls freg

freg

接下来,我们就进入到/proc目录中,首先使用cat命令读取文件freg的内容,然后使用echo命令往文件freg中写入一个新的内容,最后使用cat命令将文件freg的内容读取出来,看看是否与上次写入的内容相同。

root@android:/proc # cat freg

root@android:/proc # echo '5' > freg

root@android:/proc # cat freg

5

如果能看到上面的输出,就说明我们能够使用proc文件系统接口来访问虚拟硬件设备freg的寄存器val的内容,即说明前面所开发的驱动程序freg的功能是正确的。

最后,我们进入到/sys/class/freg/freg中,首先使用cat命令读取val文件的内容,然后使用echo命令往文件val中写入一个新的内容,最后使用cat命令将文件val中的内容读取出来,同样是检查它是否与上次写入的内容相同。

root@android:/sys/class/freg/freg # cat val

5

root@android:/sys/class/freg/freg # echo '0' > val

root@android:/sys/class/freg/freg # cat val

如果能看到上面的输出,就说明我们能够使用devfs文件系统接口来访问虚拟硬件设备freg的寄存器val的内容,同样说明前面所开发的驱动程序freg的功能是正确的。

以上两种方法只验证了驱动程序freg所提供的proc和devfs文件系统访问接口是正确的,我们还需要进一步验证它所提供的dev文件系统访问接口也是正确的,即能正常读写设备文件/dev/freg的内容。由于设备文件/dev/freg的内容是二进制格式的,因此,使用cat和echo命令来读写它的内容不够直观,在接下来的2.2小节中,我们将通过编写一个C可执行程序来直观地验证它的dev文件系统访问接口的正确性。

2.2 开发C可执行程序验证Android硬件驱动程序

在本节中,我们将通过编写一个C可执行程序来验证驱动程序freg所提供的dev文件系统接口的正确性,这是通过调用read和write函数读写设备文件/dev/freg的内容来实现的。对于Android应用程序开发者来说,可能会觉得奇怪,怎么能在Android系统中编写C语言程序呢?其实在Android源代码工程环境中,不仅可以用C/C++语言来开发可执行程序,还可以开发动态链接库,即so文件。使用adb工具命令连接上Android模拟器之后,进入到/system/bin或者/system/lib目录中,就可以看到很多可执行程序或者动态链接库文件。在接下来的2.3小节中,我们为虚拟硬件设备freg所编写的硬件抽象层模块接口其实就是一个动态链接库文件。

在Android源代码工程环境中开发的C可执行程序源文件一般保存在external目录中,因此,我们进入到external目录中,并且创建一个freg目录,用来保存我们将要开发的C可执行程序源文件。它的目录结构如下:

~/Android

----external

----freg

----freg.c

----Android.mk

这个C应用程序只有一个源文件 freg.c和一个编译脚本文件Android.mk。下面我们就详细分析这两个文件的内容。

freg.c

01 #include

02 #include

03 #include

04

05 #de fi ne FREG_DEVICE_NAME "/dev/freg"

06

07 int main(int argc, char** argv) {

08 int fd = -1;

09 int val = 0;

10

11 fd = open(FREG_DEVICE_NAME, O_RDWR);

12 if(fd == -1) {

13 printf("Failed to open device %s.\n", FREG_DEVICE_NAME);

14 return -1;

15 }

16

17 printf("Read original value:\n");

18 read(fd, &val, sizeof(val));

19 printf("%d.\n\n", val);

20

21 val = 5;

22 printf("Write value %d to %s.\n\n", val, FREG_DEVICE_NAME);

23 write(fd, &val, sizeof(val));

24

25 printf("Read the value again:\n");

26 read(fd, &val, sizeof(val));

27 printf("%d.\n\n", val);

28

29 close(fd);

30

31 return 0;

32 }

第11行通过调用open函数,并且以读写方式打开设备文件/dev/freg;接着第18行调用read函数读取它的内容,即读取虚拟硬件设备freg的寄存器val的内容,并且将它的内容打印出来。

第23行调用write函数将一个整数5写入到虚拟硬件设备freg的寄存器val中;接着第26行和第27行再调用read和print函数将这个整数5读取并且打印出来。假设虚拟硬件设备freg的寄存器val是第一次被访问,那么如果一切正常的话,两次打印出来的内容就应该分别为0和5。

Android.mk

1 LOCAL_PATH := $(call my-dir)

2 include $(CLEAR_VARS)

3 LOCAL_MODULE_TAGS := optional

4 LOCAL_MODULE := freg

5 LOCAL_SRC_FILES := $(call all-subdir-c-fi les)

6 include $(BUILD_EXECUTABLE)

这是源文件freg.c的编译脚本文件,它与使用Java语言开发的Android应用程序的编译脚本的不同之处在于include命令后面所带的参数。对于C可执行程序来说,它的编译脚本中的include命令后面跟的参数的值为$(BUILD_EXECUTABLE),表示当前要编译的是一个可执行应用程序模块,并且将编译结果保存在out/target/product/gerneric/system/bin目录中。

准备好这两个文件之后,我们就可以通过mmm和make snod命令来编译和打包这个C可执行程序了。

USER@MACHINE:~/Android$ mmm ./external/freg/

USER@MACHINE:~/Android$ make snod

编译成功后,就可以在out/target/product/gerneric/system/bin目录下看到一个freg文件;而当打包成功后,这个文件就会包含在out/target/product/gerneric目录下的Android系统镜像文件system.img中。

最后,我们就可以使用编译和打包后得到的system.img文件启动Android模拟器,然后使用adb工具连接上它,并且进入到/system/bin目录中,执行里面的freg文件来验证驱动程序freg的dev文件系统访问接口的正确性。

USER@MACHINE: ~/Android$ emulator -kernel kernel/gold fi sh/arch/arm/boot/zImage &

USER@MACHINE: ~/Android$ adb shell

root@android:/ # cd system/bin

root@android:/system/bin # ./freg

Read original value:

0.

Write value 5 to /dev/freg.

Read the value again:

5.

如果能够看到上面的输出,就说明驱动程序freg所提供的dev文件系统访问接口是正确的。

接下来,我们继续介绍如何为虚拟硬件设备freg编写硬件抽象层模块接口。

2.3 开发Android硬件抽象层模块

Android系统为硬件抽象层中的模块接口定义了编写规范,我们必须按照这个规范来编写自己的硬件模块接口,否则就会导致无法正常访问硬件。下面我们首先介绍硬件抽象层模块接口的编写规范,然后再按照这个规范为虚拟硬件设备freg开发硬件抽象层模块接口,并且分析硬件抽象层模块的加载过程,最后讨论硬件设备的访问权限问题。

2.3.1 硬件抽象层模块编写规范

Android系统的硬件抽象层以模块的形式来管理各个硬件访问接口。每一个硬件模块都对应有一个动态链接库文件,这些动态链接库文件的命令需要符合一定的规范。同时,在系统内部,每一个硬件抽象层模块都使用结构体hw_module_t来描述,而硬件设备则使用结构体hw_device_t来描述。接下来,我们就分别描述硬件抽象层模块文件的命名规范以及结构体hw_module_t和hw_device_t的定义。

2.3.1.1 硬件抽象层模块文件命名规范

硬件抽象层模块文件的命名规范定义在hardware/libhardware/hardware.c文件中,如下所示。hardware/libhardware/hardware.c

01 /**

02 * There are a set of variant fi lename for modules. The form of the fi lename

03 * is ".variant.so" so for the led module the Dream variants

04 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:

05 *

06 * led.trout.so

07 * led.msm7k.so

08 * led.ARMV6.so

09 * led.default.so

10 */

11

12 static const char *variant_keys[] = {

13 "ro.hardware", /* This goes fi rst so that it can pick up a different

14 fi le on the emulator. */

15 "ro.product.board",

16 "ro.board.platform",

17 "ro.arch"

18 };

这段代码和注释的意思是,硬件抽象层模块文件的命名规范为“.variant.so”,其中,MODULE_ID表示模块的ID,variant表示四个系统属性ro.hardware、ro.product.board、ro.board. platform和ro.arch之一。系统在加载硬件抽象层模块时,依次按照ro.hardware、ro.product.board、ro.board.platform和ro.arch的顺序来取它们的属性值。如果其中的一个系统属性存在,那么就把它的值作为variant的值,然后再检查对应的文件是否存在,如果存在,那么就找到要加载的硬件抽象层模块文件了;否则,就继续查找下一个系统属性。如果这四个系统属性都不存在,或者对应于这四个系统属性的硬件抽象层模块文件都不存在,那么就使用“.default.so”来作为要加载的硬件抽象层模块文件的名称。

系统属性ro.hardware是在系统启动时,由init进程负责设置的。它首先会读取/proc/cmdline文件,检查里面有没有一个名称为androidboot.hardware的属性,如果有,就把它的值作为属性ro.hardware的值;否则,就将/proc/cpuinfo文件的内容读取出来,并且将里面的硬件信息解析出来,即将Hardware

字段的内容作为属性ro.hardware的值。例如,在Android模拟器中,从/proc/cpuinfo文件读取出来的Hardware字段内容为goldfish,于是,init进程就会将属性ro.hardware的值设置为“goldfish”。系统属性ro.product.board、ro.board.platform和ro.arch是从/system/build.prop文件读取出来的。文件/system/build.prop 是由编译系统中的编译脚本build/core/Makefile和Shell脚本build/tools/buildinfo.sh生成的,有兴趣的读者可以研究一下这两个文件,这里就不深入分析了。

2.3.1.2 硬件抽象层模块结构体定义规范

结构体hw_module_t和hw_device_t及其相关的其他结构体定义在文件hardware/libhardware/include/ hardware/hardware.h中。下面我们就分别介绍这些结构体的定义。

hw_module_t

hardware/libhardware/include/hardware/hardware.h

01 /*

02 * Value for the hw_module_t.tag fi eld

03 */

04

05 #de fi ne MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))

06 #de fi ne HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')

07

08 /**

09 * Name of the hal_module_info

10 */

11 #de fi ne HAL_MODULE_INFO_SYM HMI

12

13 /**

14 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM

15 * and the fi elds of this data structure must begin with hw_module_t

16 * followed by module speci fi c information.

17 */

18 typedef struct hw_module_t {

19 /** tag must be initialized to HARDWARE_MODULE_TAG */

20 uint32_t tag;

21

22 /** major version number for the module */

23 uint16_t version_major;

24

25 /** minor version number of the module */

26 uint16_t version_minor;

27

28 /** Identi fi er of module */

29 const char *id;

30

31 /** Name of this module */

32 const char *name;

33

34 /** Author/owner/implementor of the module */

35 const char *author;

36

37 /** Modules methods */

38 struct hw_module_methods_t* methods;

39

40 /** module's dso */

41 void* dso;

42

43 /** padding to 128 bytes, reserved for future use */

44 uint32_t reserved[32-7];

45

46 } hw_module_t;

结构体hw_module_t中的每一个成员变量在代码中都有详细的解释,这里不再重复。不过,有五点是需要注意的。

(1)在结构体hw_module_t的定义前面有一段注释,意思是,硬件抽象层中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须为hw_module_t。

(2)硬件抽象层中的每一个模块都必须存在一个导出符号HAL_MODULE_IFNO_SYM,即“HMI”,它指向一个自定义的硬件抽象层模块结构体。后面我们在分析硬件抽象层模块的加载过程时,将会看到这个导出符号的意义。

(3)结构体hw_module_t的成员变量tag的值必须设置为HARDWARE_MODULE_TAG,即设置为一个常量值('H' <<24 | 'W'<<16 | 'M'<<8 | 'T'),用来标志这是一个硬件抽象层模块结构体。

(4)结构体hw_module_t的成员变量dso用来保存加载硬件抽象层模块后得到的句柄值。前面提到,每一个硬件抽象层模块都对应有一个动态链接库文件。加载硬件抽象层模块的过程实际上就是调用dlopen函数来加载与其对应的动态链接库文件的过程。在调用dlclose函数来卸载这个硬件抽象层模块时,要用到这个句柄值,因此,我们在加载时需要将它保存起来。

(5)结构体hw_module_t的成员变量methods定义了一个硬件抽象层模块的操作方法列表,它的类型为hw_module_methods_t,接下来我们就介绍它的定义。

hw_module_methods_t

hardware/libhardware/include/hardware/hardware.h

1 typedef struct hw_module_methods_t {

2 /** Open a speci fi c device */

3 int (*open)(const struct hw_module_t* module, const char* id,

4 struct hw_device_t** device);

5

6 } hw_module_methods_t;

结构体hw_module_methods_t只有一个成员变量,它是一个函数指针,用来打开硬件抽象层模块中的硬件设备。其中,参数module表示要打开的硬件设备所在的模块;参数id表示要打开的硬件设备的ID;参数device是一个输出参数,用来描述一个已经打开的硬件设备。由于一个硬件抽象层模块可能会包含多个硬件设备,因此,在调用结构体hw_module_methods_t的成员变量open来打开一个硬件设备时,我们需要指定它的ID。硬件抽象层中的硬件设备使用结构体hw_device_t来描述,接下来我们就介绍它的定义。

hw_device_t

hardware/libhardware/include/hardware/hardware.h

01 #de fi ne HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')

02

03 /**

04 * Every device data structure must begin with hw_device_t

05 * followed by module speci fi c public methods and attributes.

06 */

07 typedef struct hw_device_t {

08 /** tag must be initialized to HARDWARE_DEVICE_TAG */

09 uint32_t tag;

10

11 /** version number for hw_device_t */

12 uint32_t version;

13

14 /** reference to the module this device belongs to */

15 struct hw_module_t* module;

16

17 /** padding reserved for future use */

18 uint32_t reserved[12];

19

20 /** Close this device */

21 int (*close)(struct hw_device_t* device);

22

23 } hw_device_t;

结构体hw_device_t中的每一个成员变量在代码中都有详细的解释,这里不再重复。不过,有三点是需要注意的。

(1)硬件抽象层模块中的每一个硬件设备都必须自定义一个硬件设备结构体,而且它的第一个成员变量的类型必须为hw_device_t。

(2)结构体hw_device_t的成员变量tag的值必须设置为HARDWARE_DEVICE_TAG,即设置为一个常量值('H' <<24 | 'W'<<16 | 'D'<<8 | 'T'),用来标志这是一个硬件抽象层中的硬件设备结构体。

(3)结构体hw_device_t的成员变量close是一个函数指针,它用来关闭一个硬件设备。

注意

硬件抽象层中的硬件设备是由其所在的模块提供接口来打开的,而关闭则是由硬件设备自身提供接口来完成的。

至此,硬件抽象层模块接口的编写规范就介绍完了。接下来,我们就可以为虚拟硬件设备freg编写硬件抽象层模块接口了。

2.3.2 编写硬件抽象层模块接口

每一个硬件抽象层模块在内核中都对应有一个驱动程序,硬件抽象层模块就是通过这些驱动程序来访问硬件设备的,它们是通过读写设备文件来进行通信的。

硬件抽象层中的模块接口源文件一般保存在hardware/libhardware目录中。为了方便起见,我们将虚拟硬件设备freg在硬件抽象层中的模块名称定义为freg,它的目录结构如下:

~/Android/hardware/libhardware

----include

----hardware

----freg.h

Modules

----freg

----freg.cpp

----Android.mk

它由三个文件组成,其中,freg.h和freg.cpp是源代码文件,而Android.mk是模块的编译脚本文件。下面我们就分别介绍这三个文件的内容。

freg.h

01 #ifndef ANDROID_FREG_INTERFACE_H

02 #de fi ne ANDROID_FREG_INTERFACE_H

03

04 #include

05

06 __BEGIN_DECLS

07

08 /*定义模块ID*/

09 #de fi ne FREG_HARDWARE_MODULE_ID "freg"

10

11 /*定义设备ID*/

12 #de fi ne FREG_HARDWARE_DEVICE_ID "freg"

13

14 /*自定义模块结构体*/

15 struct freg_module_t {

16 struct hw_module_t common;

17 };

18

19 /*自定义设备结构体*/

20 struct freg_device_t {

21 struct hw_device_t common;

22 int fd;

23 int (*set_val)(struct freg_device_t* dev, int val);

24 int (*get_val)(struct freg_device_t* dev, int* val);

25 };

26

27 __END_DECLS

28

29 #endif

这个文件中的常量和结构体都是按照硬件抽象层模块编写规范来定义的。宏FREG_HARDWARE_ MODULE_ID和FREG_HARDWARE_DEVICE_ID分别用来描述模块ID和设备ID。结构体freg_module_t 用来描述自定义的模块结构体,它的第一个成员变量的类型为hw_module_t。结构体freg_device_t用来描述虚拟硬件设备freg,它的第一个成员变量的类型为freg_device_t。此外,结构体freg_device_t还定义了其他三个成员变量,其中,成员变量fd是一个文件描述符,用来描述打开的设备文件/dev/freg,成员变量set_val和get_val是函数指针,它们分别用来写和读虚拟硬件设备freg的寄存器val的内容。freg.cpp

这是硬件抽象层模块freg的实现文件,我们分段来阅读。

文件首先包含相关头文件并且定义相关结构体变量。

01 #de fi ne LOG_TAG "FregHALStub"

02

03 #include

04 #include

05

06 #include

07 #include

08

09 #include

10 #include

11

12 #de fi ne DEVICE_NAME "/dev/freg"

13 #de fi ne MODULE_NAME "Freg"

14 #de fi ne MODULE_AUTHOR "shyluo@https://www.docsj.com/doc/cf7084676.html,"

15

16 /*设备打开和关闭接口*/

17 static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device);

18 static int freg_device_close(struct hw_device_t* device);

19

20 /*设备寄存器读写接口*/

21 static int freg_get_val(struct freg_device_t* dev, int* val);

22 static int freg_set_val(struct freg_device_t* dev, int val);

23

24 /*定义模块操作方法结构体变量*/

25 static struct hw_module_methods_t freg_module_methods = {

26 open: freg_device_open

27 };

28

29 /*定义模块结构体变量*/

30 struct freg_module_t HAL_MODULE_INFO_SYM = {

31 common: {

32 tag: HARDWARE_MODULE_TAG,

33 version_major: 1,

34 version_minor: 0,

35 id: FREG_HARDWARE_MODULE_ID,

36 name: MODULE_NAME,

37 author: MODULE_AUTHOR,

38 methods: &freg_module_methods,

39 }

40 };

在这段代码中,最值得关注的就是模块变量HAL_MODULE_INFO_SYM的定义。按照硬件抽象层模块编写规范,每一个硬件抽象层模块必须导出一个名称为HAL_MODULE_INFO_SYM的符号,它指向一个自定义的硬件抽象层模块结构体,而且它的第一个类型为hw_module_t的成员变量的tag值必须设置为HARDWARE_MODULE_TAG。除此之外,还初始化了这个硬件抽象层模块结构体的版本号、ID、名称、作者和操作方法列表等。

虚拟硬件设备freg的打开和关闭分别由函数freg_device_open和freg_device_close来实现,如下所示。

41 static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) {

42 if(!strcmp(id, FREG_HARDWARE_DEVICE_ID)) {

43 struct freg_device_t* dev;

44

45 dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t));

46 if(!dev) {

47 LOGE("Failed to alloc space for freg_device_t.");

48 return -EFAULT;

49 }

50

51 memset(dev, 0, sizeof(struct freg_device_t));

52

53 dev->common.tag = HARDWARE_DEVICE_TAG;

54 dev->common.version = 0;

55 dev->common.module = (hw_module_t*)module;

56 dev->common.close = freg_device_close;

57 dev->set_val = freg_set_val;

58 dev->get_val = freg_get_val;

59

60 if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {

61 LOGE("Failed to open device fi le /dev/freg -- %s.", strerror(errno));

62 free(dev);

63 return -EFAULT;

64 }

65

66 *device = &(dev->common);

67

68 LOGI("Open device fi le /dev/freg successfully.");

69

70 return 0;

71 }

72

73 return -EFAULT;

74 }

75

76 static int freg_device_close(struct hw_device_t* device) {

77 struct freg_device_t* freg_device = (struct freg_device_t*)device;

78 if(freg_device) {

79 close(freg_device->fd);

80 free(freg_device);

81 }

82

83 return 0;

84 }

前面提到,一个硬件抽象层模块可能会包含多个硬件设备,而这些硬件设备的打开操作都是由函数freg_device_open来完成的,因此,函数freg_device_open会根据传进来的参数id来判断要打开哪一个硬件设备。

在硬件抽象层模块freg中,只有一个虚拟硬件设备freg,它使用结构体freg_device_t来描述。因此,函数freg_device_open发现参数id与虚拟硬件设备freg的ID值匹配以后,就会分配一个freg_device_t 结构体,并且对它的成员变量进行初始化。按照硬件抽象层模块编写规范,硬件抽象层中的硬件设备标签(dev->common.tag)必须设置为HARDWARE_DEVICE_TAG。除此之外,我们还将虚拟硬件设备freg的关闭函数设置为freg_device_close,并且将它的读写函数设置为freg_get_val和freg_set_val。

初始化完成用来描述虚拟硬件设备freg的结构体freg_device_t之后,我们就可以调用open函数来打开虚拟硬件设备文件/dev/freg了,并且将得到的文件描述符保存在结构体freg_device_t的成员变量fd中。

虚拟硬件设备freg的关闭函数freg_device_close的实现比较简单,它主要是关闭设备文件/dev/freg,以及释放设备在打开时所分配的资源。

虚拟硬件设备freg的读写函数freg_get_val和freg_set_val的实现如下所示。

85 static int freg_get_val(struct freg_device_t* dev, int* val) {

86 if(!dev) {

87 LOGE("Null dev pointer.");

88 return -EFAULT;

89 }

90

91 if(!val) {

92 LOGE("Null val pointer.");

93 return -EFAULT;

94 }

95

96 read(dev->fd, val, sizeof(*val));

97

98 LOGI("Get value %d from device fi le /dev/freg.", *val);

99

100 return 0;

101 }

102

103 static int freg_set_val(struct freg_device_t* dev, int val) {

104 if(!dev) {

105 LOGE("Null dev pointer.");

106 return -EFAULT;

107 }

108

109 LOGI("Set value %d to device fi le /dev/freg.", val);

110 write(dev->fd, &val, sizeof(val));

111

112 return 0;

113 }

这两个函数分别通过调用read和write函数来实现读写虚拟硬件设备freg的寄存器val的内容。Android.mk

1 LOCAL_PATH := $(call my-dir)

2 include $(CLEAR_VARS)

3 LOCAL_MODULE_TAGS := optional

4 LOCAL_PRELINK_MODULE := false

5 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

6 LOCAL_SHARED_LIBRARIES := liblog

7 LOCAL_SRC_FILES := freg.cpp

8 LOCAL_MODULE := freg.default

9 include $(BUILD_SHARED_LIBRARY)

Android源代码结构分析

目录 一、源代码结构 (2) 第一层次目录 (2) bionic目录 (3) bootloader目录 (5) build目录 (7) dalvik目录 (9) development目录 (9) external目录 (13) frameworks目录 (19) Hardware (20) Out (22) Kernel (22) packages目录 (22) prebuilt目录 (27) SDK (28) system目录 (28) Vendor (32)

一、源代码结构 第一层次目录 Google提供的Android包含了原始Android的目标机代码,主机编译工具、仿真环境,代码包经过解压缩后,第一级别的目录和文件如下所示: . |-- Makefile (全局的Makefile) |-- bionic (Bionic含义为仿生,这里面是一些基础的库的源代码) |-- bootloader (引导加载器),我们的是bootable, |-- build (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) |-- dalvik (JAVA虚拟机) |-- development (程序开发所需要的模板和工具) |-- external (目标机器使用的一些库) |-- frameworks (应用程序的框架层) |-- hardware (与硬件相关的库) |-- kernel (Linux2.6的源代码) |-- packages (Android的各种应用程序) |-- prebuilt (Android在各种平台下编译的预置脚本) |-- recovery (与目标的恢复功能相关) `-- system (Android的底层的一些库)

Android Hotfix 新方案——Amigo 源码解读

Android Hotfix 新方案——Amigo 源码解读 首先我们先来看看如何使用这个库。 用法 在project 的build.gradle中 dependencies { classpath 'me.ele:amigo:0.0.3' } 在module 的build.gradle中 apply plugin: 'me.ele.amigo' 就这样轻松的集成了Amigo。 生效补丁包 补丁包生效有两种方式可以选择: ? 稍后生效补丁包 ? 如果不想立即生效而是用户第二次打开App 时才打入补丁包,则可以将新的Apk 放到/data/data/{your pkg}/files/amigo/demo.apk,第二次打开时就会自动生效。可以通过这个方法 ? File hotfixApk = Amigo.getHotfixApk(context); ?

获取到新的Apk。 同时,你也可以使用Amigo 提供的工具类将你的补丁包拷贝到指定的目录当中。 ? FileUtils.copyFile(yourApkFile, amigoApkFile); ? ? 立即生效补丁包 ? 如果想要补丁包立即生效,调用以下两个方法之一,App 会立即重启, 并且打入补丁包。 ? Amigo.work(context); ? Amigo.work(context, apkFile); ? 删除补丁包 如果需要删除掉已经下好的补丁包,可以通过这个方法 Amigo.clear(context); 提示:如果apk 发生了变化,Amigo 会自动清除之前的apk。 自定义界面 在热修复的过程中会有一些耗时的操作,这些操作会在一个新的进程中的Activity 中执行,所以你可以通过以下方式来自定义这个Activity。

Struts2框架工作原理及应用体会

2012年第11卷第6期 产业与科技论坛2012.(11).6 Industrial &Science Tribune Struts2框架工作原理及应用体会 □宋 君 张家爱 【摘要】通过针对特定用户的分析,搭建以Struts2为技术核心的旅行社管理系统。本文简单的介绍了MVC 、 Struts2的工作原理,同时总结了在项目制作过程中所得到的心得。 【关键词】 Struts2;MVC ;FilterDispatcher ;Action 【基金项目】本文为大学生科技创新院级基金项目(编号:2011070)成果 【作者单位】宋君,吉林农业科技学院信息工程学院;张家爱,吉林农业科技学院信息工程学院教师 本着锻炼自我与积极参与到实用性技术的目标,以发掘自身创新意识为前提。利用空闲时间,在老师的指导下,进行了一次大学生创新项目的实践性活动。本着实用原则,以某中小旅行社为客户(根据用户需求,匿名),以Struts2框架为基点,进行了一次旅行社管理系统的开发。在项目结束之余, 特将在项目过程中经历的种种认识进行了简单的总结,希望让阅读本文的朋友们,更多的参与到此类活动中。 一、基础思想— ——MVC 简述作为时下经典框架之一, MVC 具有其独特的价值。MVC 框架简单的说,就是将数据模型与用户视图进行分离。通过控制器进行协调处理的一种结构是框架。同时,也是本文中要讨论的Sturts2框架的基础思想。 M 是指模型层(Model ),V 是指用户视图(View ),C 则是指控制器 (Controller )。这种划分方式是以将模型层与视图层进行代码分离,从而降低两者之间的耦合性,使同一程序可以使用不同形式进行表现。不同层之间的修改不会或尽量少的印象到其他层功能的史前为前提。有效的提高的代码的维护性和设计难度。 图1 二、 Struts2工作原理(一)Struts2框架组成。Struts2框架由三个主要部分组成:核心控制器、业务控制器,以及由用户实现的业务逻辑组件。这里我们将侧重于核心控制器与业务控制器的理解与说明。 (二)核心控制器:FilterDispatcher 。FilterDispatcher 是Struts2框架的核心控制器,在此,我们可以将FilterDispatcher 看作一个类似于过滤网的过滤器。当用户发出请求,并到达Web 硬哟那种时,该过滤器会过滤用户请求。如果用户请求的结尾为action ,则将该请求转入Struts2框架进行处理。当Struts2框架获得了*.actio 请求后,会根据请求前面“*”的那部分内容,决定调用哪个业务逻辑组件作为响应单位。这里需要说明的是Struts2用来处理用户请求的Action 实例并不是业务控制器,而是作为Action 的代理———正因为Struts2的一大特点,与Servlet API 的非耦合性,使得用户实现的业务控制器无法直接处理用户请求。有效的提高了后期调试维护的效率。而Struts2框架再次提供了了一系列的拦截器。这些拦截器负责将HttpServletRequest 请求的参数解析出来,传入Action 中,并毁掉Action 的Execute 方法来处理用户请求。用户实现的Action 类仅作为Struts2的Action 代理的代理目标。用户实现的业务控制器则包含了对用户请求的处理。用户的请求数据包含在HttpServletRequest 对象中,而用户的Action 类无需访问HttpServletRequest 对象。拦截器负责将HttpServletRequest 里的请求数据解析出来,并传给业务逻辑组件Action 实例。 (三)业务控制器。业务控制器就是前文提到的用来实现用户Action 的实力,这里的每个Action 类通常包含有一个execute 方法,当业务控制器处理完用户的请求后,该方法将会针对此次处理返回一个字符串— ——该字符串就是一个逻辑树图名。当程序开发人员开发出系统所需要的业务控制器后,还需要针对性的配置Struts2的Action ,即需要配置Ac- tion 的以下三个部分:(1)Action 所处理的URl 。(2)Action 组件所对应的实现类。(3)Action 里包含的逻辑试图和物理资源之间的对应关系。每个Action 都要处理一个用户请求,而用户请求则总是包含有指定的URL 。当核心控制器过滤用户请求,并调用后,根据请求的URL 和Action 处理URL 之间的对应关系来处理转发。 · 342·

Android源码下载方法详解

Android: Android源码下载方法详解 分类:Android平台 安卓源码下载地址:https://www.docsj.com/doc/cf7084676.html,/source/downloading.html 相信很多下载过内核的人都对这个很熟悉 git clone git://https://www.docsj.com/doc/cf7084676.html,/kernel/common.git kernel 但是这是在以前,现在如果这么执行的话,会显示如下内容 Initialized empty Git repository in /home/star/working/kernel/.git/ https://www.docsj.com/doc/cf7084676.html,[0: 149.20.4.77]: errno=Connection refused fatal: unable to connect a socket (Connection refused) 通过浏览器输入https://www.docsj.com/doc/cf7084676.html,/,发现该网站已经被重定向为 https://www.docsj.com/doc/cf7084676.html,/source/downloading.html 可以在该页面的最后发现内核的下载方法。 下面我们介绍一下Android源码下载的步骤。 工作环境: 操作系统:Ubuntu 10.04 或Ubuntu10.10 git程序:1.7.0.4 或1.7.1 转载请注明出处:https://www.docsj.com/doc/cf7084676.html,/pku_android 方法一: 1.1 初始化安装环境 参考网页https://www.docsj.com/doc/cf7084676.html,/source/initializing.html 主要要做的就是安装jdk和安装一些软件包 $ sudo apt-get install git-core gnupg flex bison gperf build-essential \ zip curl zlib1g-dev libc6-dev libncurses5-dev x11proto-core-dev \ libx11-dev libreadline6-dev libgl1-mesa-dev tofrodos python-markdown \ libxml2-utils 如果已经安装了,就不许要这步了 1.2 无论下载内核和源码,都需要进行如下操作 参考网页https://www.docsj.com/doc/cf7084676.html,/source/downloading.html $ mkdir ~/bin $ PATH=~/bin:$PATH $ curl https://https://www.docsj.com/doc/cf7084676.html,/dl/googlesource/git-repo/repo > ~/bin/repo 如果出现: repo init error: could not verify the tag 'v1.12.7',

yaffs2文件系统制作

交叉编译器ARM-Linux-gcc4.1.2 开发板TX2440A Busybox-1.15.1.tar.bz2(在Linux中被称为瑞士军刀) mkyaffs2image工具 首先创建一个名字为root_2.6.31的文件夹,在其中创建如下文件夹 etc bin var dev home lib mnt proc root sbin sys tmp usr opt共14个文件夹 解压Busybox tar xjvf busybox 进入源目录,修改Makefile 第164行,CROSS_COMPILE=arm-linux- 第190行,ARCH=arm 执行#make men onfig进行配置 配置选项大部分都是保持默认的,只需要注意选择以下这几个选项,其他的选项都不用动:Busybox Setting---> Build Options---> [*]Build Busybox as a static binary(no shared libs) [*]Build with Large File Support(for accessing files>2GB) Installation Options--->

(./_install)Busybox installation prefix 进入这个选项,输入busybox的安装路径,如:../rootfs Busybox Library Tuning---> [*]vi-style line editing commands [*]Fancy shell prompts 要选择这个选项:“Fancy shell prompts”,否则挂载文件系统后,无法正常显示命令提示符:“[\u@\h\W]#” 配置完成以后 执行#make #make install 然后就会在上一级目录下生成rootfs文件夹,里面包含几个文件夹/bin/sbin/usr linuxrc 把这些文件全部复制到刚建好的root_2.6.31目录下, #cp–rf*../root_2.6.31 在dev目录下,创建两个设备节点: #mknod console c51 #mknod null c13 然后进入自己建立的etc目录 拷贝Busybox-1.15.2/examples/bootfloopy/etc/*到当前目录下。 #cp-r../../busybox-1.15.2/examples/bootfloopy/etc/*./ 包括文件:fstab init.d inittab profile

linux-2.6.18移植

Linux-2.6.18移植 有了我们的交叉编译环境和我们先前学的内核基础知识,下面我们就开始我们的内核移植了,我们所用的是博创的 S3C2410 。 关于 linux-2.6.18.tar.bz2 的下载网站先前我们说过,我们要先到该官方网站上去下载一个全新的内核。 [root@Binnary ~ ]# tar –jxvf linux-2.6.18.tar.bz2 [root@Binnary ~ ]# make mrproper 如果你是新下载的内核,那这一步就不用了。但如果你用的是别人移植好的内核,那最好在编译内核之前先清除一下中间文件,因为你们用来编译内核的交叉编译工具可能不同。 第一步:修改Makefile文件 将 改为 第二步:修改分区设置信息 我们要先在BootLoader中查看相应的分区信息 vivi>help 然后修改内核源码中的分区信息。分区信息文件在 a rch/arm/mach-s3c2410/common-smdk.c 将其中的

改为如下内容:

第三步:内核通过 BootLoader把数据写入NAND Flash,而vivi的ECC效验算法和内核的不同,内核的效验码是由NAND Flash控制器产生的,所以在此必须禁用NAND Flash ECC。所以我们就要修改 drivers/mtd/nand/s3c2410.c 这个文件。将 中的 chip->ecc.mode = NAND_ECC_SOFT ,改为如下 chip->ecc.mode = NAND_ECC_NONE。

只此一处。 第四步:下面是devfs的问题,因为2.6.12内核以后取消了devfs的配置选项,缺少了它内核会找不到mtdblock设备。所以我们需要修改 fs/Kconfig 文件,或者是从2.6.12的fs/Kconfig中拷贝下面几项到2.6.18的fs/Kconfig中去,我们采用修改的方法来完成。 修改 fs/Kconfig支持devfs 。 在Pseudo filesystems 主菜单的最后添加我们所要的内容。 第五步:文件系统的支持 Yaffs 文件系统 YAFFS文件系统简介 YAFFS,Yet Another Flash File System,是一种类似于JFFS/JFFS2的专门为Flash设计 的嵌入式文件系统。与JFFS相比,它减少了一些功能,因此速度更快、占用内存更少。 YAFFS和JFFS都提供了写均衡,垃圾收集等底层操作。它们的不同之处在于: (1)、JFFS是一种日志文件系统,通过日志机制保证文件系统的稳定性。YAFFS仅仅 借鉴了日志系统的思想,不提供日志机能,所以稳定性不如JAFFS,但是资源占用少。 (2)、JFFS中使用多级链表管理需要回收的脏块,并且使用系统生成伪随机变量决定 要回收的块,通过这种方法能提供较好的写均衡,在YAFFS中是从头到尾对块搜索, 所以在垃圾收集上JFFS的速度慢,但是能延长NAND的寿命。 (3)、JFFS支持文件压缩,适合存储容量较小的系统;YAFFS不支持压缩,更适合存 储容量大的系统。 YAFFS还带有NAND芯片驱动,并为嵌入式系统提供了直接访问文件系统的API,用 户可以不使用Linux中的MTD和VFS,直接对文件进行操作。NAND Flash大多采用 MTD+YAFFS的模式。MTD( Memory Technology Devices,内存技术设备)是对Flash 操作的接口,提供了一系列的标准函数,将硬件驱动设计和系统程序设计分开。 Yaffs 文件系统内核没有集成,可以对其主页下载: https://www.docsj.com/doc/cf7084676.html,/cgi-bin/viewcvs.cgi/#dirlist

Android USB 驱动分析

Android USB 驱动分析 一、USB驱动代码架构和使用 1、代码简介 USB驱动代码在/drivers/usb/gadget下,有三个文件:android.c, f_adb.c, f_mass_storage.c;g_android.ko 是由这三个文件编译而来,其中android.c 依赖于 f_adb.c 和 f_mass_storage.c(这两个文件之间无依赖关系)。 可在android.c中看到: static int __init android_bind_config(struct usb_configuration *c) { struct android_dev *dev = _android_dev; int ret; printk(KERN_DEBUG "android_bind_config\n"); ret = mass_storage_function_add(dev->cdev, c, dev->nluns); if (ret) return ret; return adb_function_add(dev->cdev, c); } 2、驱动使用 要使USB mass storage连接到主机: 打开/sys/devices/platform/usb_mass_storage/lun0/file文件,向 file文件写入一个存储 设备的路径,例如/dev/block/vold/179:0 (major:minor)路径; 这里的usb_mass_storage根据实际应用可以改的,由 platform_device_register函数的参数决 定。 例如: static struct platform_device fsg_platform_device = { .name = "usb_mass_storage", .id = -1, }; static void __init tegra_machine_init(void) { .... (void) platform_device_register(&fsg_platform_device); .... }

struts2 实验报告

1.系统分析与设计 1.1 系统功能描述 本系统是个非常简单的注册、登录系统。本系统的实现是基于Struts2、Spring、Hibernate 三个框架,系统功能单一,业务逻辑简单。 当用户注册信用户时,就是向系统中增加一个新用户,对应的数据库增加一条记录。 当用户输入注册信息时,系统提供了基本的输入验证判断用户输入是否合法,只有当用户输入满足基本输入要求时,才会被提交到实际的登录系统,进行实际的登录处理。 系统还使用了随机产生的图形验证码来防止刷新,防止用户通过单击浏览器的书安心按钮来重复注册多个用户。 系统还提供了一种Ajax方式来验证用户输入的注册名是否有效,系统要求所有的用户名不能重复。故当用户输完用户名后,系统立即在页面上方提示用户该用户名是否可用,如果系统中没有该用户名,则系统提示该用户名可用;否则提示用户该用户名重复,用户必须重新选择用户名注册。 当用户注册一个新用户名之后,就可以使用系统的登录功能来登录系统了,用户输入登录用的用户名、密码后,系统一样提供了基本的输入校验。 除此之外,系统还采用了随机产生图形验证码来防止恶意用户的暴力破解,系统随机生成一个图形验证码,而用户登录必须输入图形验证码中显示的字符串,只有用户输入的字符串和系统随机生成的验证码字符相同时,系统才允许用户登录。 1.2 系统功能流程

1.3 数据库设计 相关的映射文件: 一旦提供了上面的映射文件,Hibernate 就可以理解User 和user_table 之间的对应关系。 2.系统实现与测试 2.1 系统采用的关键技术 MVC 框架采用了Struts2框架,Struts2框架的易用性,极好的简化了系统的MVC 层的实现;本系统使用了Struts2的JSON 插件来完成Ajax 功能,除此之外本系统为了避免进行底层的Ajax 交互,还是用了一个简单Prototype.js 函数库,用以简化Ajax 编程。Struts2框架的稳定性,为系统的稳定运行提供了保证。

Yaffs2文件系统中对NAND Flash磨损均衡的改进

Yaffs2文件系统中对NAND Flash磨损均衡的改进 摘要:针对以NAND Flash为存储介质时Yaffs2文件系统存在磨损均衡的缺陷,通过改进回收块选择机制,并在数据更新中引入冷热数据分离策略,从而改善NAND Flash的磨损均衡性能。实验借助Qemu软件建立Linux嵌入式仿真平台,从总擦除次数、最大最小擦除次数差值和块擦除次数标准差等方面进行对比。实验结果表明,在改进后的Yaffs2文件系统下NAND Flash的磨损均衡效果有明显提升,这有益于延长NAND Flash的使用寿命。 关键词: Yaffs2文件系统;NAND Flash;垃圾回收;冷热数据 0 引言 NAND Flash存储设备与传统机械磁盘相比,具有体积小、存储密度高、随机存储和读写能力强、抗震抗摔、功耗低等特点[1]。它被广泛用于智能手机、车载智能中心、平板电脑等智能终端中。近年来,以NAND Flash为存储介质的固态硬盘也得到越来越多的应用。目前Yaffs2文件系统(Yet Another Flash File System Two,Yaffs2)[1]是使用最多、可移植性最好的专用文件系统,在安卓、阿里云OS、Linux等嵌入式系统中都有使用。在Yaffs2文件系统下以NAND Flash为存储介质时存在磨损均衡的缺陷,可通过对回收块选择机制作改进和引入冷热数据分离策略来提高磨损均衡的效果。 1 Yaffs2和Nand Flash关系 这里以使用最多的Linux操作系统为实践,将Yaffs2文件系统移植到Linux操作系统中。Linux系统通常可以分为3层:应用层、内核层和设备层,其中支持NAND Flash设备的Yaffs2文件系统属于内核层,。 最上层用户应用程序通过VFS(Virtual File System)提供的统一接口,将数据更新等文件操作传递给Yaffs2。VFS代表虚拟文件系统,它为上层应用提供统一的接口。有了这些接口,应用程序只用遵循抽象后的访问规则,而不必理会底层文件系统和物理构成上的差异。然后Yaffs2通过MTD(Memory Technology Device)提供的统一访问接口对NAND Flash进行读、写和擦除操作,从而完成数据的更新或者存储操作。MTD代表内存技术设备,它为存储设备提供统一访问的接口。最终,在NAND Flash上以怎样的格式组织和存储数据由Yaffs2文件系统决定。 NAND Flash由若干块(block)组成,每个块又是由若干页(page)组成,页中含有数据区和附加区。NAND Flash的页根据状态不同,可以分为有效页、脏页、空闲页。有效页中存放有效数据,脏页中存放无效数据,空闲页是经过擦除后可以直接用于写入数据的页。NAND Flash在写入数据前需要执行擦除操作,因此数据不能直接在相同的位置更新。当一个页中数据需要更新时,必须将该页中有效数据拷贝到其他空闲页上再更新,并将原来页上的数据置为无效。随着时间的推移,许多无效页累积在存储器中使得空闲页逐渐减少。当存储器中的空闲空间不足时,启动垃圾回收操作,利用回收块选择机制从待回收块中选取满足要求的块来擦除,从而得到足够的空闲空间。NAND Flash中块的擦除次数有限,通常为10 000次~100 000次[2]。当某个块的擦除次数超过使用寿命时,该块将无法正常用于数据存储。因此,垃圾回收应利用合理的回收块选择机制,从待回收块中找到回收后能产生良好磨损均衡效果且付出较少额外代价的块来回收,从而获得足够的空闲空间用于数据更新操作。 2 Yaffs2在磨损均衡方面的缺陷 Yaffs2中回收块的选择机制[3]是从待回收块中找到有效数据最少的块来回收。回收过程中,Yaffs2能够减少有效数据的额外读和写操作。当数据更新处于均匀分布的情况下,Yaffs2表现出较好的磨损均衡效果。 但是,通常情况下数据的更新频率不同,有些数据经常更新,而有些数据很少更新。经

Android 串口编程原理和实现方式附源码

提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:。下面我分别对JNI、以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。 串口编程需要了解的基本知识点:对于串口编程,我们只需对串口进行一系列的设置,然后打开串口,这些操作我们可以参考串口调试助手的源码进行学习。在Java中如果要实现串口的读写功能只需操作文件设备类:即可,其他的事都由驱动来完成不用多管!当然,你想了解,那就得看驱动代码了。这里并不打算对驱动进行说明,只初略阐述应用层的实现方式。 (一)JNI: 关于JNI的文章网上有很多,不再多做解释,想详细了解的朋友可以查看云中漫步的技术文章,写得很好,分析也很全面,那么在这篇拙文中我强调3点: 1、如何将编译好的SO文件打包到APK中?(方法很简单,直接在工程目录下新建文件夹libs/armeabi,将SO文件Copy到此目录即可) 2、命名要注意的地方?(在编译好的SO文件中,将文件重命名为:lib即可。其中是编译好后生成的文件) 3、MakeFile文件的编写(不用多说,可以直接参考package/apps目录下用到JNI的相关项目写法) 这是关键的代码: [cpp]view plaincopy

(二):

文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主要实际用途是创建一个包含该结构的或。这是API的描述,不太好理解,其实可简单的理解为:就是对一个文件进行读写。 (三)实现串口通信细节 1) 建工程:SerialDemo包名:org.winplus.serial,并在工程目录下新建jni和libs两个文件夹和一个org.winplus.serial.utils,如下图: 2) 新建一个类:SerialPortFinder,添加如下代码: [java]view plaincopy 1.package org.winplus.serial.utils; 2. 3.import java.io.File; 4.import java.io.; 5.import java.io.IOException; 6.import java.io.LineNumberReader; 7.import java.util.Iterator; 8.import java.util.Vector; 9. 10.import android.util.Log; 11. 12.public class SerialPortFinder { 13. 14.private static final String TAG = "SerialPort"; 15.

如何看懂源代码--(分析源代码方法)

如何看懂源代码--(分析源代码方法) 4 推 荐 由于今日计划着要看Struts 开源框架的源代码 昨天看了一个小时稍微有点头绪,可是这个速度本人表示非常不满意,先去找了下资 料, 觉得不错... 摘自(繁体中文 Traditional Chinese):http://203.208.39.132/translate_c?hl=zh-CN&sl=en&tl=zh-CN&u=http://ww https://www.docsj.com/doc/cf7084676.html,/itadm/article.php%3Fc%3D47717&prev=hp&rurl=https://www.docsj.com/doc/cf7084676.html,&usg=AL kJrhh4NPO-l6S3OZZlc5hOcEQGQ0nwKA 下文为经过Google翻译过的简体中文版: 我们在写程式时,有不少时间都是在看别人的代码。 例如看小组的代码,看小组整合的守则,若一开始没规划怎么看,就会“噜看噜苦(台语)”不管是参考也好,从开源抓下来研究也好,为了了解箇中含意,在有限的时间下,不免会对庞大的源代码解读感到压力。网路上有一篇关于分析看代码的方法,做为程式设计师的您,不妨参考看看,换个角度来分析。也能更有效率的解读你想要的程式码片段。 六个章节: ( 1 )读懂程式码,使心法皆为我所用。( 2 )摸清架构,便可轻松掌握全貌。( 3 )优质工具在手,读懂程式非难事。( 4 )望文生义,进而推敲组件的作用。( 5 )找到程式入口,再由上而下抽丝剥茧。( 6 )阅读的乐趣,透过程式码认识作者。 程式码是别人写的,只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感,深怕被迫去碰触其他人所写的程式码。但是,与其抗拒接收别人的程式码,不如彻底了解相关的语言和惯例,当成是培养自我实力的基石。 对大多数的程式人来说,撰写程式码或许是令人开心的一件事情,但我相信,有更多人视阅读他人所写成的程式码为畏途。许多人宁可自己重新写过一遍程式码,也不愿意接收别人的程式码,进而修正错误,维护它们,甚至加强功能。 这其中的关键究竟在何处呢?若是一语道破,其实也很简单,程式码是别人写的,只有原作者才真的了解程式码的用途及涵义。许多程式人心里都有一种不自觉的恐惧感,深怕被迫去碰触其他人所写的程式码。这是来自于人类内心深处对于陌生事物的原始恐惧。 读懂别人写的程式码,让你收获满满 不过,基于许多现实的原因,程式人时常受迫要去接收别人的程式码。例如,同事离职了,必须接手他遗留下来的工作,也有可能你是刚进部门的菜鸟,而同事经验值够了,升级了,风水轮流转,一代菜鸟换菜鸟。甚至,你的公司所承接的专案,必须接手或是整合客户前一个厂商所遗留下来的系统,你们手上只有那套系统的原始码(运气好时,还有数量不等的文件)。 诸如此类的故事,其实时常在程式人身边或身上持续上演着。许多程式人都将接手他人的程式码,当做一件悲惨的事情。每个人都不想接手别人所撰写的程式码,因为不想花时间去探索,宁可将生产力花在产生新的程式码,而不是耗费在了解这些程式码上。

2-Linux

Linux-2.6.32.2内核在mini2440上的移植(二)---yaffs2文件系统移植 移植环境(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容) 2.1, yaffs2文件系统移植 【1】获取yaffs2 源代码 现在大部分开发板都可以支持yaffs2 文件系统,它是专门针对嵌入式设备,特别是使用nand flash 作为存储器的嵌入式设备而创建的一种文件系统,早先的yaffs 仅支持小页(512byte/page)的nand flash,现在的开发板大都配备了更大容量的nand flash,它们一般是大页模式的(2K/page),使用yaffs2 就可以支持大页的nand flash,下面是yaffs2 的移植详细步骤。 在https://www.docsj.com/doc/cf7084676.html,/node/346可以下载到最新的yaffs2 源代码,需要使用git工具( 安装方法见Git版本控制软件安装与使用),在命令行输入: [root@localhost ~]# cd ./linux-test [root@localhost linux-test]# git clone git://https://www.docsj.com/doc/cf7084676.html,/ya ffs2 Cloning into yaffs2... remote: Counting objects: 6592, done. remote: Compressing objects: 100% (3881/3881), done. remote: Total 6592 (delta 5237), reused 3396 (delta 2642) Receiving objects: 100% (6592/6592), 3.34 MiB | 166 KiB/s, d one. Resolving deltas: 100% (5237/5237), done.

App工程结构搭建:几种常见Android代码架构分析

App工程结构搭建:几种常见Android代码架构分析 关于Android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角度,看到整齐的代码,优美的分层总是一种舒服的享受的。 从艺术的角度看,其实我们是在追求一种美。 本文先分析几个当今比较流行的android软件包,最后我们汲取其中觉得优秀的部分,搭建我们自己的通用android工程模板。 1. 微盘 微盘的架构比较简单,我把最基本,最主干的画了出来: 第一层:com.sina.VDisk:com.sina(公司域名)+app(应用程序名称) 。 第二层:各模块名称(主模块VDiskClient和实体模块entities)第三层:各模块下具体子包,实现类。 从图中我们能得出上述分析中一个最简单最经典的结构,一般在应用程序包下放一些全局的包或者类,如果有多个大的模块,可以分成多个包,其中包括一个主模块。 在主模块中定义基类,比如BaseActivity等,如果主模块下还有子模块,可以在主模块下建立子模块相应的包。说明一点,有的时候如果只有一个主模块,我们完全可以省略掉模

块这一层,就是BaseActivity.java及其子模块直接提至第二层。 在实体模块中,本应该定义且只定义相应的实体类,供全局调用(然而实际情况可能不是这样,后面会说到)。在微盘应用中,几乎所有的实体类是以xxx+info命名的,这种命名也是我赞成的一种命名,从语义上我觉得xxxModel.java这种命名更生动更真实,xxxModel给我一种太机械太死板的感觉,这点完全是个人观点,具体操作中以个人习惯为主。还有一点,在具体的xxxInfo,java中有很多实体类中是没有get/set的方法,而是直接使用public的字段名。这一点,我是推荐这种方式的,特别是在移动开发中,get/set方法很多时候是完全没有必要的,而且是有性能消耗的。当然如果需要对字段设置一定的控制,get/set方法也是可以酌情使用的。 2. 久忆日记 相比于微盘的工程结构,久忆日记的结构稍微复杂了一些。如下图: 1).第一层和前面微盘一样的. 2).第二层则没有模块分类,直接把需要的具体实现类都放在下面,主要日记的一些日记相关的Activity。 3).第二层的实体包命令为model包,里面不仅存放了实体类

Struts2的工作机制原理分析及实例

Struts2的工作机制分析及实例 一、概述 本章讲述Struts2的工作原理。 读者如果曾经学习过Struts1.x或者有过Struts1.x的开发经验,那么千万不要想当然地以为这一章可以跳过。实际上Struts1.x 与Struts2并无我们想象的血缘关系。虽然Struts2的开发小组极力保留Struts1.x的习惯,但因为Struts2的核心设计完全改变,从思想到设计到工作流程,都有了很大的不同。 Struts2是Struts社区和WebWork社区的共同成果,我们甚至可以说,Struts2是WebWork的升级版,他采用的正是WebWork 的核心,所以,Struts2并不是一个不成熟的产品,相反,构建在WebWork基础之上的Struts2是一个运行稳定、性能优异、设计成熟的WEB框架。 本章主要对Struts的源代码进行分析,因为Struts2与WebWork的关系如此密不可分,因此,读者需要下载xwork的源代码,访问https://www.docsj.com/doc/cf7084676.html,/xwork/download.action即可自行下载。 下载的Struts2源代码文件是一个名叫struts-2.1.0-src.zip的压缩包,里面的目录和文件非常多,读者可以定位到 struts-2.1.0-src\struts-2.0.10\src\core\src\main\java目录下查看Struts2的源文件,如图14所示。 (图14) 二、主要的包和类 Struts2框架的正常运行,除了占核心地位的xwork的支持以外,Struts2本身也提供了许多类,这些类被分门别类组织到不同的包中。从源代码中发现,基本上每一个Struts2类都访问了WebWork提供的功能,从而也可以看出Struts2与WebWork千丝万缕的联系。但无论如何,Struts2的核心功能比如将请求委托给哪个Action处理都是由xwork完成的,Struts2只是在WebWork 的基础上做了适当的简化、加强和封装,并少量保留Struts1.x中的习惯。

Struts2考试题分析

题目1 以下不届丁 Struts2中result 的type 届性() ? A. action B. redirect 题目2 下歹0有关拦截器说法错误的是? 「A.struts 通过拦截器完成执行action 请求处理方法前一系歹U 操作。例如: 数据封装、文件上传、数据校验等 'B.在struts 中,直接访问jsp 页面,struts 将使用默认拦截器栈处理当前 请求。 厂C.在执行action 时,struts 将执行若干拦截器1、2、3,执行action 完成 后,将继续执行拦截器3、2、1 'D.默认情况,在一个action 没有配置拦截器的引用,说明当前action 将不 使用拦截器 题目3 以下哪些是Action 接口提供的返回值? W A A. success ,D B. none C. error 财 D.input 题目4 如果要实现struts2的数据检验功能 广A 普通的Action 类可以实现 C. redirectAction D. dispatcher

「B继承自Action接口的可以实现 面C继承自ActionSupport类可以实现 厂D继承自ActionValidate 类可以实现 题目5 struts2默认的处理结果类型是: ? A.dispatcher ' B.redirect 「C.chain D. forward 题目6 在值栈的上下文Context中,存在一些固定的key表示不同的对象,以下描述正确的是? A. request,表示request作用域的数据 'B.session,表示session 作用域的数据 阿 C.application ,表示application 作用域的数据 * D.parameters ,表示请求参数的所有数据 题目7 以下届丁struts2配置文件中的配置元素是:()多选) A. B. 厂 C. 厂 D.

使用yaffs2img工具制作Android刷机包教程

制作刷机包 打开‘yaffs2img浏览器’,点击左上角的‘选取yaffs2文件’选择你刚刚复制出来的 files文件夹里的system.img 先来认识一下这个软件 1.定制软件的提取(此部和制作刷机包没关系,可以不做,想用官方软件的同学可以 看看) 选择app,右键你想要提取软件,提取就可以了,我是把整个app文件夹提取出来了,不用 的软件直接删掉好了 2.定制软件的精简 在你不想要用的软件上直接右键,删除,就好了,你也可以右键添加你想要用的软件,得把

软件改成比较简短的英文名,否则有可能不能用 秀一下我精简后的列表,大家可以参照着精简 https://www.docsj.com/doc/cf7084676.html,uncher文件的替换 下载好你想要用的桌面软件,改名为‘Launcher’,删掉app中的‘Launcher2’,添加进去你改好名字的‘Launcher’就好了,我比较喜欢ADW,所以我把ADW的文件名改为 Launcher,替换掉原来的Launcher2就好了 4.破音问题的解决 在左边导航点选‘etc’,右键添加文件,把附件中的声音配置文件解压出来 ‘AudioFilter.csv’添加进去就好了 AudioFilter.rar (355 Bytes)

5.字体的更改 下载字体文件,中文字体库一律把名字改名为‘DroidSans Fallback.ttf’,英文字体改为‘DroidSans.ttf ’,加粗的英文字体改为‘DroidSans-Bold.ttf ’然后再左边导航栏点选‘fonts’,把之前自带的字体删除,然后把你改好名字的字体添加进去就好了把国产机皇的字体也分享给大家,中文+英文+英文加粗 6.开机音乐和照相机音乐的删除 在导航栏点选‘media’,在audio/ui文件夹下,删除‘Bootsound.mp3’(开机音乐)和

相关文档
相关文档 最新文档