Android深度探索(卷2):系统应用源代码分析与ROM定制

978-7-115-36794-5
作者: 李宁
译者:
编辑: 张涛

图书目录:

详情

国内第一本详细分析定制Android ROM的著作,详细分析了Android系统应用的源代码,使读者可以更深入了解Android内部应用是如何做到其他程序无法做到的事,并且从这些源代码中可以获得很多对开发应用程序有益的帮助。

图书摘要

Android深度探索(卷2):系统应用源代码分析与ROM定制
李宁 编著
人民邮电出版社

北京

图书在版编目(CIP)数据

Android深度探索.第2卷,系统应用源代码分析与ROM定制/李宁编著.--北京:人民邮电出版社,2015.1

ISBN 978-7-115-36794-5

Ⅰ.①A… Ⅱ.①李… Ⅲ.①移动终端—应用程序—程序设计 Ⅳ.①TN929.53

中国版本图书馆CIP数据核字(2014)第206350号

内容提要

本书专门介绍Android操作系统的编程,全书分为两部分。第一部分主要介绍Android源代码和Linux内核源代码的下载和编译、Root权限的提取、Android ROM 的制作和刷机、Recovery的定制等。第二部分主要分析Android系统应用的实现原理和源代码。这些系统应用包括Android应用的安装和卸载管理、系统设置、系统设置内容提供者、电话与联系人管理、短信与彩信管理、Launcher2和NFC后台服务程序。通过对这些Android系统应用的源代码分析,会使读者定制出更完美的Android ROM。

本书主要采用了CM10.1源代码进行讲解分析,使读者可以系统和完整地掌握定制Android ROM所需要的最新技术。无论读者是想找一份好工作,还是想满足自己的Geek(极客)情节,本书都是您的首先。

本书适合具备一定的开发经验(最好有Java或C/C++开发经验)、想学习Android和Linux底层开发的程序员使用,也适合具备一定的Android开发经验,想从事底层开发的编程爱好者使用。本书还适合作为相关培训学校的Android底层开发培训教材。

◆编著 李宁

责任编辑 张涛

责任印制 彭志环

◆人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

北京鑫正大印刷有限公司印刷

◆开本:787×1092 1/16

印张:31.75

字数:839千字  2015年1月第1版

印数:1-3500册  2015年1月北京第1次印刷

定价:89.00元(附光盘)

读者服务热线:(010)81055410 印装质量热线:(010)81055316

反盗版热线:(010)81055315

广告经营许可证:京崇工商广字第0021号

前言

为什么要写这本书

开源软件在很多年前就存在了,不过自从Google公司发布开源的Android以来,好像突然关注这个开源系统的人多了起来。而且与其他开源系统不同,很多 Android 的关注者不但使用Android,还不断对Android“动手动脚”。这些人通过不断修改Android源代码的各个部分,制作出了很多Android的衍生版本,也就是我们经常说的ROM。

就目前而言,无论国外,还是国内;无论大企业,还是小公司,甚至是小团队和个人,都在疯狂地制作各种类型的ROM。例如,有一定规模的CM(Cyanogenmod) ROM、HTC ROM、三星 ROM 等;还有国内的小米 ROM(MIUI ROM)、点心 ROM、乐蛙 ROM,当然还有赶鸭子上架的老罗ROM(更准确地说应该是锤子ROM)。这些ROM各有特色,很难说哪个更有优势。

既然有这么多 ROM,可能很多搞 Android 开发的程序员按耐不住了。因为对于从没搞过ROM 或对 ROM 一知半解的程序员来说,ROM 往往显得很神秘,而且很高深。所以,这些好奇的程序员中的绝大多数都曾想满足一下自己的好奇心,制作一款完全属于自己的ROM(强烈的好奇心和求知欲望是人类的高贵品质,程序员尤为如此)。当然,部分想尝试做ROM的程序员还听说这类工作的薪水比较高,而且好找工作,这也许是很多程序员想学习做 ROM 的另外一个原因吧!不过在心动到行动的过程中,会发现从网上收集到的制作 ROM 的资料都是零散的,有些甚至是错误的,而且非常不系统。所以,这些想进入 ROM 世界的程序员再一次进入迷茫状态。

如果手捧本书的您正在读这段前言,说明迷茫状态快要结束了!为了满足有志于制作 ROM的程序员们的好奇心。笔者特意编写了本书。意在系统地阐述如何从Android源代码制作一个完整的 ROM,以及各种刷机技术。并且可以更深层次地定制 ROM。例如,修改Android Home 应用Launcher2,使其更符合自己的苛刻要求。

本书的内容

通常定制ROM分为应用层和底层(主要指驱动层),不过,由于大多数ROM是基于CM ROM的,而 CM ROM 已经将底层的驱动适配得非常好了,支持目前大多数主流机型。而且由于国内大多数第三方的ROM并没有自己适配底层驱动,所以,本书将把主要精力放在定制应用层上。

关于应用层的定制通常分为 Android 系统应用和 Framework。前者主要指直接面向用户的Android应用,例如,Home应用(Launcher2)、短信管理、系统设置等。而后者主要面对程序员群体。例如,为ROM增加某些特有的API,以及修改原有的API,使其满足某些特殊的要求。由于篇幅所限。本书将主要讨论Android系统应用的定制。在《Android深度探索》系列的后续著作中会继续讨论Framework的定制。

本书分为如下两部分。

(1)Android ROM的制作和刷机(第1章~第5章)。

(2)Android系统应用源代码分析和定制(第6章~第15章)。

其中第一部分主要包括如下几个方面。

(1)开发环境搭建以及Android 源代码(官方Android 源代码和CM Android 源代码)和Linux内核源代码的下载和编译。

(2)Bootloader 和Recovery ROM 的制作和刷机。

(3)提取Root权限的原理和实践(包括am命令详解、Superuser和su的源代码分析等)。

(4)ROM包含的各种镜像(system.img、boot.img、recovery.img等)的修改、制作和刷机技巧。这里的制作主要指从Android源代码制作。

(5)Recovery ROM 的核心:Edify语言。

(6)集成第三方的APK 程序(包括Google Services Framework和Google Play)。

(7)Recovery的原理和定制(包括为Recovery增加新功能,汉化Recovery等)。

第二部分主要包括如下内容。

(1)如何开发和测试Android系统应用。

(2)Android应用程序的安装、卸载原理和PackageInstaller代码分析。

(3)系统设置的各种功能的实现原理和源代码分析。

(4)系统内容提供者的源代码分析。

(5)电话与联系人的实现原理和源代码分析。

(6)短信与彩信管理的实现原理和源代码分析。

(7)Android Home 应用(Launcher2)的实现原理和源代码分析。

(8)近场通信(NFC)后端服务程序的实现原理和源代码分析。

要注意的是,本书将以CM 10.1(Android 4.x)源代码作为基础进行讲解。

本书的特点

(1)系统地阐述了Android ROM的制作和刷机过程。

(2)全书使用较新的CM10.1(Android 4.x)。

(3)完全采用Ubuntu Linux12.04 LTS作为实验环境

(4)分析了 Android 中主要的系统应用的实现原理和源代码,使读者充分掌握定制 Android系统应用的方法。

(5)分析了制作ROM过程中涉及的核心技术的实现原理,例如,Root权限的提取、Recovery定制等。使读者可以制作出更酷的Android ROM。

(6)由于本书分析了大量的源代码,所以,还详细介绍了分析源代码的工具和一些技巧。

读者对象

从事Android应用开发,但想进入Android底层开发领域的程序员。

对定制Android ROM感兴趣的程序员。

想进一步提高Android底层开发技术和实践能力的程序员。

开设Android底层开发课程的大专院校和培训机构。

想成为Geek(极客),但苦于没有人指导的Android爱好者。

源代码和工具下载

读者可以到http://pan.baidu.com/s/19UeDO 下载CM10.1(Android 4.x)的源代码。本书涉及的其他源代码和工具都可以在随书光盘中找到。另外可以到http://blog.csdn.net/nokiaguy下载光盘中的内容。

相关的视频课程为:http://edu.51cto.com/lecturer/user_id-974126.html。

勘误和支持

由于作者的水平有限,编写时间仓促,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如有问题或建议,请发送至techcast@126.com或在新浪微博(http://weibo.com/638012593)上留言。非常期待能够得到你们的真挚反馈。编辑联系邮箱:zhangtao@ptpress.com.cn。

致谢

感谢所有在本书写作过程中给予我指导、帮助和鼓励的朋友,尤其是人民邮电出版社的编辑张涛,不仅对本书提出了宝贵的写作建议,而且还对本书进行了仔细的审阅。

感谢一直以来信任、鼓励、支持我的家人和朋友。

感谢eoeandroid、移动开发者社区的朋友对我技术上的帮助。

谨以此书献给我最亲爱的家人,以及众多热爱Android的朋友们!

编者

第6章 Android 系统应用的开发与测试

可能很多读者对开发Android 应用的流程和方法已经很熟悉了,不过在本书随后的部分要着重介绍的Android系统应用可能还会有些陌生。实际上,Android系统应用与Android应用都是APK程序,但大多数 Android 系统应用与 Android 应用有一定的区别。主要区别就是 Android 系统应用是被嵌入到Android ROM 中的,尽管Android 应用也可被嵌入到Android ROM 中,但Android 系统应用可以调用Android SDK的内部API,而这一点Android应用是做不到的。当然,Android系统应用还有其他特点,这些内容将在本章详细描述。为了使读者能更容易理解Android 系统应用的开发和测试过程,本章会分析最简单的一个Android系统应用:计算器。通过该程序,我们会对Android系统应该有一个初步的认识。

6.1 什么是Android 系统应用

当第一次拿到新的Android手机时,就会发现手机中已经预先安装了很多应用,这些Android应用一般不是通过普通的方法安装到手机上的,而是直接嵌入到 ROM 中,并刷到手机上的。通过这种方式安装的Android应用是无法使用常规的方法卸载的。除非在root权限下删除系统目录的APK文件,或在Recovery或Bootloader模式下通过刷机的方式去除这些程序,否则是无法从手机中删除Android系统应用的。

如果按着常规的Android应用安装方法,需要较长的时间,而且还会出现一个权限提示对话框。但我们发现,Google Play中安装程序时并没有出现任何提示框,最多只是在状态栏显示一条提示信息。实际上,这种现象充分说明了Android系统应用的另外一个特性:拥有更大的权限。由于Google Play是Android 系统应用,所以即使在没有root权限的情况下,Google Play 也可以读写/data/app目录[1] ,所以只需要将APK文件复制到这个目录即可安装程序,根本就不需要通过PackageInstaller[2] 进行安装。

综上所述,Android系统应用具有如下几个特点,这些特点都是普通的Android应用不具备的。

嵌入到Android ROM中,通过普通的方法无法卸载这些程序。

拥有更高的权限。例如,可以实现静默安装。

可以调用Android SDK 的私有API,这些API 在ADT开发环境下无法调用。

6.2 为什么要研究Android 系统应用

可能有很多人认为 Android 系统应用本质上就是普通的 Android 应用,并没有像 Android Framework、HAL、Linux驱动有研究价值。实际上,这种想法是大错特错了。没错!Android系统应用从程序的结构来看,与普通的 Android 应用非常类似,不过这些程序的价值远非普通的Android应用可比,因为有很多直接与Android应用交互的接口都是在这些系统应用中实现的。也就是说,了解了这些系统应用,就意味着对Android系统最上层的API有一个非常透彻的了解。

在Android源代码中有一个packages目录,所有的Android系统应用的源代码都在这个目录中。其中packages/app是最核心的目录,所有内嵌的APK程序都在该目录中。如果读者要加入自己的Android系统应用,也需要将APK文件放到这个目录中。如果想了解Android系统有哪些窗口(Activity)可用,app目录中包含的源代码将告诉我们一切。

除了app目录外,其他一些目录,例如,providers,也非常重要,尤其是providers目录,会告诉我们整个Android 系统有哪些Content Provider可供调用,并且会得知详细的使用方法。当然,我们还可以从Android系统应用的源代码中得到更多的信息。例如,开发过桌面小部件的读者都知道,桌面小部件不支持将EditText控件放到桌面上,那么Android桌面的部件是如何做的呢?不过研究 Android 系统应用源代码还有一个更重要的目的,就是定制 ROM。像现在很多流行的ROM,如HTC的Sense UI、小米的仿iPhone的UI,以及老罗模仿与创新并存的锤子ROM,都需要对Android系统应用的源代码相当了解实现(当然还要有很好的UI设计师才行)。

所以研究Android系统应用的源代码好处多多,综合起来读者至少从以下几个方面可以获益。

了解Android系统中有哪些窗口、Content Provider、Service和Broadcast可以与之交互。

充分掌握很多高级应用的使用方法,例如,OTA 升级是如何实现的。

对实现原理比较感兴趣的读者可以通过这些源代码了解像Launcher2、短信管理等应用的内容构造。

对于想编写可以完成系统级操作的应用的读者,可以学会如何将 Android 应用嵌入到ROM,升级为Android系统应用,进而可以做任何自己想做的事。

对于那些有极客情结的读者,完全定制自己的ROM是最令人振奋的梦想,而Android 系统应用将是实现这一梦想最重要的基石。

从 6.3 节开始,我们将逐步深入 Android 系统应用的开发和测试过程,读者将从中体会到完全控制Android设备的快感和成就感!

6.3 如何编写Android系统应用

源代码目录:src/ch06/FirstSystemApp

Android系统应用和普通的Android应用基本相同,都是主要用Java语言编写的APK程序。不过前者与后者有如下两点不同。

签名不同。Android 系统应用使用的是系统签名,或称为平台(Platform)签名,而普通的Android应用使用的是一般的签名。

可以访问的API 不同。在Android SDK 中有很多API(类、接口、方法等)都声明为hide [3] 。这类API不允许在普通的Android应用中访问,而只有经过Platform签名的Android系统应用才能使用这些API。

除了以上两点,Android系统应用和普通应用的创建过程基本相同,只是由于Android系统应用可能访问系统级的API(hide API),而ADT 是不会识别这些API 的,所以在ADT+Eclipse 开发环境导入Android系统应用的源代码后会发现使用这些API的地方都用红色波浪线标注了。不过这不要紧,反正也不在IDE中编译和运行Android系统应用。

在了解了Android系统应用和普通的Android应用的区别后,面临的下一个问题就是如何编译和运行Android系统应用呢?

Android系统应用完全可以像普通的Android应用一样在Eclipse中编辑,IDE的基本特性仍然会保留,但由于使用了系统 API,所以是不能直接编译的。要想编译 Android 系统应用,必须要依靠 Android 源代码(最好是已经进行一遍完整编译的 Android 源代码)才可以。通常会将Android 系统应用的源代码放到<Android 源代码根目录>/packages/apps 目录或其子目录中。编译Android系统应用开需要在程序的根目录建立一个Android.mk文件,该文件是Android源代码专用的编译文件,相当于GCC的Makefile文件。至于该文件的内容,通常并不需要有更深入的了解,只需要在apps目录中找一个Android系统应用,如PackageInstaller,将该程序中Android.mk文件复制一份,然后进行适当修改即可。

本节提供了一个普通的 Android 应用(FirstSystemApp),该程序并未调用任何系统 API,所以可以直接在Eclipse中编译和运行。不过只要加入了Android.mk文件,就可以将FirstSystemApp程序变成 Android 系统应用,并安装到 Android 设备的/system/app[4] 目录中。下面先看一下FirstSystemApp程序的Android.mk文件的内容。

src/ch06/FirstSystemApp/Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES :=$(call all-java-files-under, src)

LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4

# 将编译生成FirstSystemApp.apk 文件

LOCAL_PACKAGE_NAME := FirstSystemApp

LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

Android.mk文件的大多数内容都是标准的写法,只有下面两行代码需要了解一下。

LOCAL_PACKAGE_NAME := FirstSystemApp

LOCAL_CERTIFICATE := platform

其中第一行需要根据不同的 Android 系统应用进行修改。系统会根据 LOCAL_PACKAGE_NAME变量的值生成APK文件,例如,本例该变量的值是FirstSystemApp,所以会在out/target/product/maguro/system/app目录生成FirstSystemApp.apk文件,如果Android设备已经获取了root权限,直接将该文件使用adb push命令上传到/system/app 目录即可。如果Android 设备没有root权限,就只能通过刷机的方式安装系统应用了。要注意的是 FirstSystemApp.apk 不能通过普通应用的方式进行安装。LOCAL_CERTIFICATE变量表示签名类型,系统应用通常设为platform,表示Platform签名。

在随书光盘中提供的Android源代码已经包含了本节的例子,读者需要在Linux终端进入如下的目录,并执行mm命令即可编译FirstSystemApp。

<Android源代码根目录>/packages/apps/ch06/FirstSystemApp

如果当前的Linux终端为进行Android的初始化,需要在Android源代码本目录中执行如下的命令进行初始化,否则无法执行mm命令。

# source ./build/envsetup.sh

# lunch

为了方便读者,在本书中实现的每一个Android系统应用根目录都带了一个update.sh脚本文件,只需要执行 source update.sh 命令,就会自动编译和上传当前的系统应用到 Android 设备的/system/app目录。

扩展学习:Android系统的签名

Android源代码中包含了4 个签名,前面使用的platform就是其中之一。这些签名文件中在如下的目录中。

<Android源代码根目录>/build/target/product/security

下面解释一下这4个前面的含义。

testkey:普通的Key,默认情况下使用。如果Android.mk文件不设置LOCAL_CERTIFICATE 变量,就使用该签名。

platform :使用该签名的系统应用可以执行平台的核心功能。

shared :使用该签名的系统应用可以和home/contacts 进程共享数据。

media :使用该签名的系统应用将成为media/download 系统的一部分。

LOCAL_CERTIFICATE 变量可以指定上述4 个值,例如,如果值为platform,会使用security目录的platform.pk8文件对APK文件进行签名。如果值为media,会使用media.pk8文件对APK文件进行签名。

6.4 分析第一个Android 系统应用:计算器

本节会分析一个比较简单的Android系统应用:计算器。这个系统应用几乎被包含在所有的ROM中,也是最常用的Android系统应用之一。计算器尽管从功能上看并不复杂,但实际的实现代码还是比较多的,不过大多数代码都用于控制各种效果、布局、历史处理等。这些部分的实现与普通的Android应用没什么不同,所以本节会直接深入计算器的核心:计算表达式。因为不管计算器的外观多绚丽,最终的目的都是为了计算表达式,所以本节的主要任务就是找到计算器应用中计算表达式的部分,然后对该计算器做一些扩展,使外部的应用可以利用计算器的功能计算表达式。

6.4.1 计算器应用(Calculator)的基本结构

本节主要看一下计算器应用的基本结构。计算器应用在Android源代码中的位置如下:

<Android源代码根目录>/packages/apps/Calculator

读者可以直接将Calculator导入Eclipse,然后查看其实现代码。Calculator共有13个类,如图6-1所示。不过这些类并不需要都详细查看其代码,因为大多数类只是用于渲染显示效果的,所以我们只关注与核心功能紧密相关的类。

为了找到主窗口类,通常会首先查看AndroidManifest.xml文件的内容,在该文件中只看到定义了一个窗口,代码如下:

apps/Calculator/AndroidManifest.xml

<activity android:name="Calculator"

android:theme="@android:style/Theme.Holo.NoActionBar"

android:windowSoftInputMode="stateAlwaysHidden">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.DEFAULT" />

<category android:name="android.intent.category.LAUNCHER" />

<category android:name="android.intent.category.APP_CALCULATOR" />

</intent-filter>

</activity>

从AndroidManifest.xml文件的代码中可以得到如下两个信息。

计算器的主窗口类是Calculator。

可以通过android.intent.category.APP_CALCULATOR调用计算器。

一会再来分析Calculator窗口类,现在先来看看如何调用计算器。由于在定义Calculator窗口类时提供了一个叫android.intent.category.APP_CALCULATOR的Category,所以在任何的Android应用中都可以使用该Category直接定位Calculator,并显示计算器窗口,代码如下:

Intent intent = new Intent("android.intent.action.MAIN");

intent.addCategory("android.intent.category.APP_CALCULATOR");

startActivity(intent);

尽管可以不加Category,不过系统中会含有很多Action为android.intent.action.MAIN的窗口,所以去掉intent.addCategory("android.intent.category.APP_CALCULATOR"),就会显示类似如图6-2所示的选择列表,为了直接定位到Calculator,一般会添加专有的Category。

现在来寻找计算表达式的部分。首先打开Calculator.java文件的代码,该文件的代码比较多,不过大多数代码都与核心功能没多大关系。但在 Calculator 类中涉及了如下两个类,对于计算表达式非常重要。

CalculatorDisplay:用于显示表达式的计算结果,也就是计算器上方用于显示表达式或计算结果的控件类。

Logic:用于计算表达式的类。

这两个类分别对应的变量是mDisplay和mLogic。这两个变量都在Calculator.onCreate方法中完成了创建和初始化工作,代码如下:

apps/Calculator/src/com/android/calculator2/Calculator.java

public void onCreate(Bundle state)

{

super.onCreate(state);

……

mDisplay = (CalculatorDisplay) findViewById(R.id.display);

mLogic = new Logic(this, mHistory, mDisplay);

mLogic.setListener(this);

mLogic.setDeleteMode(mPersist.getDeleteMode());

mLogic.setLineLength(mDisplay.getMaxDigits());

……

mDisplay.setOnKeyListener(mListener);

……

}

对于CalculatorDisplay类,并不用太关注它,该类提供了getText和setText方法分别用来获取和设置显示的文本。现在主要关注Logic类。该类提供了一个evaluate方法,可以将表达式字符串传入该方法,并以字符串形式返回计算结果。该方法的原型如下:

String evaluate(String input) throws SyntaxException;

其中input参数表示表达式字符串。如果要在其他Android应用中利用Calculator的计算表达式功能,只要调用该方法即可。不过标准的Calculator将这一功能封死了,无法创建Logic类并调用evaluate方法。在6.4.3小节会对Calculator进行扩展,允许通过Uri可以传入表达式,并在计算器窗口上直接显示计算结果。

至于 evaluate 方法内部是如何计算表达式的,这就涉及了一些编译原理的知识,这些知识已超出了本书的范围,所以在这里不再讨论。不过读者除非要实现自己的计算器[5] ,否则一般并不需要了解这些内容,只要知道如何调用即可。

6.4.2 编译、测试和调试 Calculator

不管处于何种目的,很多研究Android源代码的程序员都会修改原有的代码来满足自己的需要。如果读者对这些感兴趣,可以试着修改Calculator的源代码,然后在Linux终端进入Calculator目录,并执行mm命令编译Calculator。但要注意,在第一次编译Calculator之前,需要执行下面的命令初始化Android源代码的一些命令(包括mm、mmm等),并选择相应的Target。

# source ./build/envsetup.sh

# lunch

编译后,会在如下的目录生成Calculator.apk文件[6]

<Android源代码根目录>/out/target/product/maguro/system/app

如果读者的Android设备已经有了root权限,可以直接使用adb push命令将Calculator.apk 文件上传到Android设备的/system/app目录中。如果没有root权限,就只能使用ROM在Recovery或Bootloader模式下通过刷机的方式将Calculator.apk文件放到/system/app目录了。关于刷机的细节在前面几章进行了不同层次的分析,读者可以参阅这些章节的内容。

由于Android系统应用不能直接在ADT+Eclipse开发环境下进行调试,所以只能通过输出日志信息的方式进行调试。例如,Log.d(…,…)。

注意

在上传APK文件到/system/app目录时,如果该APK文件是独立的,也就是说不带odex,应将/system/app目录中对应APK文件的odex文件删除,否则这些应用仍然会执行odex中的代码。关于生产odex文件的方法会在下一章详细讨论。

6.4.3 允许其他 Android 应用通过 Calculator计算表达式

源代码目录:src/ch06/Calculator

本节会对Calculator进行改造,加入允许其他Android应用通过Uri传递表达式字符串,并在Calculator主窗口显示计算结果的功能。

Intent Filter会使用如下3 个条件进行过滤。

action:一个Intent Filter只能有一个Action。

category:一个Intent Filter 可以有多个Category。

data:一个Intent Filter 可以有多个Data。

action和category可以理解为字符串。也就是说一个窗口可以和一个主字符串(action)绑定,并且还可跟多个辅助字符串(category)。data实际上相当于Uri。也就是说,一个窗口还可以与一个Uri绑定。如果在定义窗口时同时指定了action、category和data,这3个条件都要满足才会通过对该窗口的过滤。在6.4.1小节的Calculator窗口的定义代码中看到,只指定了action和category,所以为了给Calculator添加一个用于传递表达式的Uri,需要指定一个data。不过要注意,不要修改原来的Intent Fitler,应该新增加一个Intent Filter。这么做的主要原因是其他的Android 应用可能会使用到原来的 Intent Filter,如果改变了原来 Intent Filter 的过滤条件,就意味着所有调用Calculator的程序都会抛出异常了。经笔者测试,如果修改了原来的Intent Filter,Calculator甚至无法在程序列表中显示。

为了与原来的 Calculator 程序对比,本节修改了 Calculator 的 Package Name,使其与原来的Calculator可以共存。新的Package Name 是com.android.calculator2.ext。在src/ch06 目录中的是修改了Package Name的Calculator。在随书带的Android源代码中也包含了修改后的Calculator程序,路径如下:

<Android源代码本目录>/packages/apps/ch06/Calculator

读者可以在Linux终端进入Calculator目录,并执行source update.sh命令编译Calculator,其中update.sh脚本文件中包含了mm命令和上传APK文件的命令。新版的Calculator编译生成的APK 文件名是 CalculatorExt.apk。修改编译生成的 APK 文件名只需修改 Calculator 目录中的Android.mk文件中的如下代码即可。

LOCAL_PACKAGE_NAME := CalculatorExt

现在来看一下新的计算器程序的Calculator窗口类的定义代码。

<activity android:name="Calculator"

android:theme="@android:style/Theme.Holo.NoActionBar"

android:windowSoftInputMode="stateAlwaysHidden">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.DEFAULT" />

<category android:name="android.intent.category.LAUNCHER" />

<category android:name="android.intent.category.APP_CALCULATOR" />

</intent-filter>

<!-- 新增加的Intent Filter -->

<intent-filter>

<action android:name="android.intent.action.APP_CALCULATOR" />

<category android:name="android.intent.category.DEFAULT" />

<data android:scheme="cal"/>

</intent-filter>

</activity>

在新增加的Intent Filter 中添加了如下3个过滤条件。

action:android.intent.action.APP_CALCULATOR。

category:android.intent.category.DEFAULT。

data:scheme="cal"。

只要将CalculatorExt.apk安装到/system/app目录中,当前系统的任何Android应用都可以使用下面的代码计算表达式。

Intent intent = new Intent("android.intent.action.APP_CALCULATOR");

// 通过Uri 传递要计算的表达式

intent.setData(Uri.parse("cal:4*(3+6*20)"));

startActivity(intent);

现在对Calculator进行最后一步的修改,也就是修改Calculator.onCreate方法的代码。该代码的作用是获取表达式字符串,并调用Logic.evaluate方法计算表达式,最后将计算结果显示在窗口上,也就是CalculatorDisplay控件中,具体的实现代码如下:

public void onCreate(Bundle state)

{

super.onCreate(state);

… …

try

{

// 计算表达式,并实现计算结果

mDisplay.setText(mLogic.evaluate(getIntent().getData().getSchemeSpecificPart()),Scroll.NONE);

}

catch (Exception e)

{

// 如果表达式错误,显示出错信息

mDisplay.setText("expression error", Scroll.NONE);

}

}

现在执行source update.sh命令编译上传CalculatorExt.apk 文件,然后在其他的Android 应用中使用前面的方式向 Calculator 窗口传递要计算的表达式。如果表达式正确,就会显示如图 6-3所示的计算结果。如果表达式错误,就会显示如图6-4所示的信息。

6.5 小结

本章阐述了研究Android系统应用的必要性。不管是定制ROM,还是寻找隐藏的API,或是为了理解 API 的原理,Android 系统应用源代码都是获取信息的第一手资料。为了使读者有更深的体会,本章还分析了一个比较简单的 Android 系统应用:Calculator。尽管没有分析 Calculator的每一个组成部分,但已经将Calculator的核心提炼了出来,并且对Calculator进行了扩展,使任何其他的Android应用都可以直接利用Calculator中的表达式计算功能。可能读者从Calculator中获取的信息并不多,除了计算表达式外好像也没有什么有价值的信息,不过不要担心,Calculator只是最简单的Android系统应用,其中有价值的信息非常有限。对于那些复杂的系统应用,如系统设置、Launcher2等,其中的信息将是海量的,在后面的章节中将完成对这些复杂的Android系统应用的分析,并通过扩展这些系统应用使读者可以更好地理解它们所涉及的技术,并可随心所欲地驾驭Android。

注 释

[1]. 所有Android 应用的APK文件都放在这个目录。

[2]. PackageInstaller 也是一个 Android 系统应用。当安装程序时,系统会先调用 PackageInstaller,该程序会先检测待安装的程序拥有的权限,然后会显示一个我们经常在安装程序时看到的权限提示对话框,然后单击该对话框的“确定”按钮就可以继续安装程序了。

[3]. 由于 Android SDK 中某些 API 非常危险,例如,可以静默安装、卸载程序、拦截电话等,所以 Google 公司在设计这些 API时将其声明为hide,以便只有在Android系统应用中才能使用。这些Android系统应用不能通过常规方法安装,只能在有root权限的情况下上传到Android设备的/system/app目录,或随ROM一起发布,还可以通过刷机(升级包)的方法安装这些应用。这就限制了这些危险API的使用,从而大大降低使用Android系统的风险。

[4]. 要注意的是Android 源代码中存储系统应用的目录是apps,而Android 设置中存储二进制形式系统应用的目录是/system/app,它们差一个“s”,不要弄混了!

[5]. 现在有很多生成词法和语法源代码的工具,如 lexer、yacc、javacc、antlr 等。所以实现一个复杂的计算表达式的程序并没有我们想象得复杂,读者可以参考相关的资料,通常只需要很少的代码即可实现。

[6]. 该目录会根据 lunch 命令选择的 Target 目录而不同,例如,本例选择的 Target 是 maguro,该 Target 适合 Galaxy Nexus 系列Android设备,如果读者选择其他的Target,maguro就会变成其他的名称。

相关图书

Android App开发入门与实战
Android App开发入门与实战
Kotlin入门与实战
Kotlin入门与实战
Android 并发开发
Android 并发开发
Android APP开发实战——从规划到上线全程详解
Android APP开发实战——从规划到上线全程详解
Android应用案例开发大全( 第4版)
Android应用案例开发大全( 第4版)
深入理解Android内核设计思想(第2版)(上下册)
深入理解Android内核设计思想(第2版)(上下册)

相关文章

相关课程