Android-SL4A-脚本编程高级教程-一-

298 阅读1小时+

Android SL4A 脚本编程高级教程(一)

原文:Pro Android Scripting with SL4A

协议:CC BY-NC-SA 4.0

零、前言

众所周知,传统的计算模式正在经历一场彻底的变革。处理能力不断增强的智能手机的普及只会加速这一过程。平板设备作为智能手机平台的延伸,已经得到了更广泛的采用,而以前缩小通用计算机尺寸的尝试失败了。虽然从用户的角度来看,最流行的移动设备的操作系统可能有所不同,但它与桌面系统的共同点比你想象的要多。

谷歌的 Android 平台在过去的一年里有了巨大的增长,并正在挑战苹果 iOS 的市场份额。苹果在应用方面的广泛领先优势一直在稳步缩小,尽管在质量方面还没有定论。在很大程度上,构建这些应用仅限于 iOS 的 Objective C 和 Android 的 Java。如果考虑到 MonoTouch 和 MonoDroid 项目,还有一些其他选择,但仅此而已。

移动设备可能永远不会完全取代传统电脑,尽管活动的分工将继续向你接触最多的那一方倾斜。这本书是关于以编写简单程序或脚本来完成特定任务的形式,将你从台式计算机中获得的一些灵活性带给你。我知道我一路走来学到了很多,我真诚地希望通过阅读这本书,你也能收集到一些东西。

一、简介

这本书是关于主要使用 Python 语言和一点点 JavaScript 为 Android 平台编写真实世界的应用。虽然 Java 没有任何问题,但当你需要做的只是打开或关闭 Android 设备上的一些设置时,它真的是大材小用了。Android 脚本层(SL4A)项目就是为了满足这一特定需求而启动的。这本书将向您介绍 SL4A,并让您能够以您从未想过的方式自动化您的 Android 设备。

为什么选择 SL4A?

你对这本书的第一个问题可能是,“为什么我要用 SL4A 而不是 Java?”这个问题有几个答案。一是不是每个人都是 Java 的粉丝。Java 语言对一些人来说太重了,而且不完全是开源的。它还需要使用编辑/编译/运行设计循环,这对于简单的应用来说可能是乏味的。一个同样合理的答案是“我想使用 X”,其中 X 可以是任何数量的流行语言。

Google 提供了一个全面的软件开发工具包(SDK ),专门针对 Java 开发人员,Android 市场上的大多数应用可能都是用 Java 编写的。我将在第三章中讲述 Android SDK,并在整本书中使用它附带的一些工具。

images 注意 SL4A 目前支持 Beanshell、JRuby、Lua、Perl、PHP、Python、Rhino。

SL4A 的真正目标是那些寻找一种方法来编写简单的脚本以在使用任何支持的语言的 Android 设备上自动化任务的人,包括通过 Beanshell 的 Java。它提供了一个交互式控制台,您可以在其中键入一行代码并立即看到结果。在许多情况下,它甚至可以重用您为桌面环境编写的代码。底线是 SL4A 使得用 Java 以外的语言为基于 Android 的设备编写代码和以一种更具交互性的方式编写代码成为可能。

安卓的世界

2005 年,谷歌收购安卓公司,大举进军移动操作系统领域。它在如此短的时间内取得了如此大的进步,真是令人惊讶。Android 社区非常庞大,已经产生了大量的会议、书籍和支持材料,这些都可以在互联网上轻松获得。

这是定义几个术语的好时机,你将在本书的其余部分看到这些术语。Android 应用通常被打包成.apk文件。这些实际上只是包含应用所需的一切的.zip文件。事实上,如果你将一个.apk文件重命名为.zip,,你可以用任何存档工具打开它并检查其内容。

大多数 Android 设备来自制造商,其系统文件受到保护,以防止任何无意或恶意的操作。Android 操作系统(OS)本质上是以 Linux 为核心的,它提供了许多与任何 Linux 桌面相同的功能。有很多方法可以解锁系统区域,并提供对 Android 设备上整个文件系统的根用户访问。这个过程被恰当地称为你的设备,一旦完成,该设备被描述为。SL4A 不需要根设备,但是如果您选择了这个路径,它将在一个根设备上工作。

Android 应用剖析

Android 基于 Linux 操作系统(在撰写本文时,Linux 内核的版本为 2.6)。Linux 提供了所有的核心管道,如设备驱动程序、内存和进程管理、网络堆栈和安全性。内核还在硬件和应用之间增加了一个抽象层。用一个解剖学的比喻来说,您可能会认为 Linux 是机器人身体的骨骼、肌肉和器官。

Android 堆栈的下一层是 Dalvik 虚拟机(DVM)。这一部分提供了核心的 Java 语言支持和 Java 编程语言的大部分功能。DVM 是大脑,大部分处理工作都在其中进行。每个 Android 应用都在 DVM 的私有实例中运行在自己的进程空间中。应用框架提供了 Android 应用所需的所有必要组件。来自 Google Android 文档:

“开发人员可以完全访问核心应用使用的相同框架 API。应用架构旨在简化组件的重用。任何应用都可以发布其功能,然后任何其他应用都可以利用这些功能(受框架实施的安全约束的约束)。同样的机制允许用户更换组件。

所有应用的基础是一组服务和系统,包括:

  • 一组丰富且可扩展的视图,可用于构建应用,包括列表、网格、文本框、按钮,甚至嵌入式网络浏览器
  • 使应用能够从其他应用(如联系人)访问数据或共享自己数据的内容供应器
  • 资源管理器,提供对本地化字符串、图形和布局文件等非代码资源的访问
  • 一个通知管理器,允许所有应用在状态栏显示自定义提醒
  • 一个活动管理器,管理应用的生命周期,并提供一个通用的导航 back stack "1

所有 Android 应用都基于三个核心组件:活动、服务和接收者。这些核心组件通过名为 intents 的消息激活。SL4A 通过其 API facade 为您提供了许多核心 Android 功能,因此了解一些基础知识是个不错的主意。第三章和第五章详细看 Android SDK 和 Android 应用编程接口(API),具体的我留到后面再说。现在,我将向您介绍活动和意图,因为它们将被广泛使用。

活动

Android 文档将活动定义为“一个应用组件,提供一个用户可以与之交互的屏幕,以便做一些事情,例如拨打电话、拍照、发送电子邮件或查看地图。每个活动都有一个窗口,可以在其中绘制用户界面。窗口通常会充满整个屏幕,但也可能会比屏幕小,并浮动在其他窗口的顶部。

Android 应用由一个或多个松散耦合在一起的活动组成。每个应用通常都有一个“主”活动,该活动可以依次启动其他活动来完成不同的功能。

意图

来自 Google 文档:“意图是一个简单的消息对象,它表示做某事的意图。例如,如果您的应用想要显示一个网页,它通过创建一个意图实例并将其交给系统来表达其查看 URI 的意图。系统会找到其他一些知道如何处理这个意图的代码(在本例中是浏览器)并运行它。意图还可以用于在系统范围内广播有趣的事件(如通知)。”

可以使用一个 intent 和startActivity来启动一个活动broadcastIntent将其发送给任何感兴趣的BroadcastReceiver组件,startService(Intent)bindService(Intent, ServiceConnection, int)与后台服务进行通信。意图使用您必须以参数形式提供的主要和次要属性。

有两个主要属性:

  • **动作:**将要执行的一般动作,如VIEW_ACTIONEDIT_ACTIONMAIN_ACTION
  • **数据:**要操作的数据,比如联系人数据库中的人员记录,表示为统一资源标识符(URI)

1

二级属性有四种类型:

  • **类别:**给出关于要执行的动作的附加信息。例如,LAUNCHER_CATEGORY意味着它应该作为顶层应用出现在启动器中,而ALTERNATIVE_CATEGORY意味着它应该包含在用户可以对一段数据执行的替代操作列表中。
  • **类型:**指定意图数据的显式类型(MIME 类型)。通常,类型是从数据本身推断出来的。通过设置此属性,可以禁用该计算并强制显式类型。
  • component: 指定一个组件类的显式名称,用于 intent。通常,这是通过查看意图中的其他信息(动作、数据/类型和类别)并将其与可以处理它的组件进行匹配来确定的。如果设置了该属性,则不执行任何计算,并且该组件完全按原样使用。通过指定该属性,所有其他意图属性都成为可选属性。
  • extras: 任何附加信息的捆绑包。这可用于向组件提供扩展信息。例如,如果我们有一个发送电子邮件的动作,我们也可以在这里包含额外的数据片段,以提供主题、正文等等。

SL4A 历史

SL4A 于 2009 年 6 月在 Google 开源博客上首次公布,最初命名为 Android 脚本环境(ASE)。主要是通过达蒙·科勒的努力,这个项目才得以实现。随着项目的不断成熟,其他人也做出了贡献。在撰写本文时,最新的版本是 r4,尽管您也可以在 SL4A 网站([code.google.com/p/android-scripting](http://code.google.com/p/android-scripting))上找到实验版本。

SL4A 架构

在最底层,SL4A 本质上是一个脚本主机,这意味着作为一个应用,它托管不同的解释器,每个解释器处理一种特定的语言。如果您要浏览 SL4A 源代码库,您会看到每种语言的源代码树的副本。使用 Android 原生开发工具包(NDK)为 ARM 架构进行交叉编译,并在 SL4A 启动特定解释器时作为库加载。此时,脚本将被逐行解释。

SL4A 的基本架构类似于您在分布式计算环境中看到的。图 1-1 以图示的形式显示了当你启动 SL4A 然后运行一个脚本(在本例中为hello.py))时的执行流程。每个 SL4A 脚本都必须导入或获取一个外部文件,比如 Python 的android.py,它将定义许多与 Android API 通信所需的代理函数。

SL4A 和底层 Android 操作系统之间的实际通信使用远程过程调用(RPC)机制和 JavaScript 对象符号(JSON)。您通常会在分布式体系结构中发现 RPC,在这种体系结构中,信息在客户机和服务器之间传递。以 SL4A 为例,服务器是 Android OS,客户端是 SL4A 脚本。这在 SL4A 和 Android 操作系统之间增加了一层隔离,以防止任何恶意脚本做任何有害的事情。

安全性是一个问题,也是 SL4A 使用 RPC 机制的原因之一。SL4A wiki 是这样描述的:

" RPC 身份验证: SL4A 通过要求所有脚本由相应的 RPC 服务器进行身份验证来强制执行每个脚本的安全沙箱。为了使认证成功,脚本必须向相应的服务器发送正确的握手秘密。这是通过完成的

  1. 读取AP_HANDSHAKE环境变量。
  2. 用值AP_HANDSHAKE作为参数调用 RPC 方法_authenticate

_authenticate方法必须是第一个 RPC 调用,并且应该在 Android 库初始化期间发生。例如,参见 Rhino 的或 Python 的 Android 模块”。 2

images

图 1-1。 SL4A 执行流程图


2

SL4A 概念

在我们实际使用 SL4A 之前,有许多概念需要介绍。在很高的层次上,SL4A 提供了许多协同工作的功能部件。每种受支持的语言都有一个解释器,可以在 Android 平台上运行。伴随解释器的是 Android API 的抽象层。这个抽象层以每种语言所期望的形式提供了一个调用接口。解释器和原生 Android API 之间的实际通信使用进程间通信(IPC)作为额外的保护层。最后,它支持在设备上交互测试脚本的环境。

虽然图 1-1 显示 Python 是解释器,但是这个概念对于所有支持的语言都是一样的。每个解释器在自己的进程中执行语言,直到进行 API 调用。然后使用 RPC 机制将其传递给 Android 操作系统。解释器和 Android API 之间的所有通信通常都使用 JSON 来传递信息。

JavaScript 对象符号(JSON)

SL4A 大量使用 JSON 来传递信息。如果您以前从未见过 JSON,您可能想访问一下[www.json.org](http://www.json.org)网站。最简单的 JSON 只是一种定义数据结构或对象的方式,就像在程序环境中一样。在大多数情况下,您会看到 JSON 结构以一系列名称/值对的形式出现。名称部分将始终是一个字符串,而值可以是任何 JavaScript 对象。

在 SL4A 中,您会发现许多 API 调用使用 JSON 返回信息。幸运的是,在创建、解析和使用 JSON 时有多种选择。Python 将 JSON 视为一等公民,拥有完整的工具库,可以在 JSON 和其他本地 Python 类型之间来回转换。Python 标准库pprint模块是一种以可读性更好的格式显示 JSON 响应内容的便捷方式。

Python 标准库包括一个 JSON 模块,其中有许多方法可以使处理 JSON 变得更加容易。因为 JSON 对象可以包含几乎任何类型的数据,所以必须使用编码器和解码器将原生 Python 数据类型转换成 JSON 对象。这是通过json.JSONEncoderjson.JSONDecoder方法完成的。当您将一个 JSON 对象从一个地方移动到另一个地方时,您必须序列化然后反序列化该对象。这需要用json.load()json.loads()函数进行解码,用json.dump()json.dumps()进行编码。

有大量的 web 服务已经采用 JSON 作为实现 API 的标准方法。这里有一个来自雅虎的图片:

{ "Image": { "Width":800, "Height":600, "Title":"View from 15th Floor", "Thumbnail": { "Url":"http:\/\/scd.mm-b1.yimg.com\/image\/481989943", "Height": 125, "Width": "100" }, "IDs":[ 116, 943, 234, 38793 ] } }

事件

Android 操作系统使用事件队列作为处理特定硬件生成的动作的手段,例如当用户按下某个硬件键时。其他可能性包括任何设备传感器,例如加速度计、GPS 接收器、光传感器、磁力计和触摸屏。在检索信息之前,必须明确打开每个传感器。

SL4A API facade 提供了许多 API 调用,这些调用将启动导致事件的某种类型的操作。其中包括以下内容:

  • startLocating()
  • startSensing()
  • startTrackingPhoneState()
  • startTrackingSignalStrengths()

这些调用中的每一个都将开始收集某种类型的数据,并生成诸如“位置”事件或“电话”事件之类的事件。任何受支持的语言都可以注册一个事件处理程序来处理每个事件。startLocating()调用有两个参数,允许您指定更新之间的最小距离和最小时间。

语言

SL4A 带来了许多语言选择。在撰写本书时,这些选择包括 Beanshell、Lua、JRuby Perl、PHP、Python 和 Rhino(在下面的章节中给出了这些版本)。如果愿意,您还可以编写或重用 shell 脚本。毫无疑问,所有这些语言中最流行的是 Python。到目前为止,对其他语言的支持还没有接近 Python 的水平,但是如果你愿意的话,也可以使用它们。

Beanshell 2.0b4

Beanshell 是一种有趣的语言,因为它基本上是用 Java 解释的。这有点回避了这样一个问题:当你可以使用 Android SDK 编写原生 Java 时,你为什么想要一个解释的 Java 呢?Beanshell 解释器确实提供了一个编写和测试代码的交互式工具。它肯定不会是最快的代码,但是您可能会发现它对于测试代码片段很有用,而不需要经历整个编译/部署/测试周期。

检查android.bsh文件显示了用于设置 JSON 数据结构的代码,这些数据结构用于向 Android 操作系统传递信息和从 Android 操作系统接收信息。下面是基本调用函数的样子:

call(String method, JSONArray params) { JSONObject request = new JSONObject(); request.put("id", id); request.put("method", method); request.put("params", params); out.write(request.toString() + "\n"); out.flush(); String data = in.readLine();

if (data == null) { return null; } return new JSONObject(data); }

这里有一个简单的hello_world.bsh脚本:

source("/sdcard/com.googlecode.bshforandroid/extras/bsh/android.bsh"); droid = Android(); droid.call("makeToast", "Hello, Android!");

月球 5.1.4

Lua.org 将 Lua 描述为“一种扩展编程语言,旨在用数据描述工具支持通用过程编程”。 3 术语扩展编程语言的意思是 Lua 意在通过脚本来扩展现有程序。这很符合 SL4A 的理念。

从语法角度来看,Lua 有点像 Python,因为它不使用花括号来包装代码块,也不需要分号来结束语句,尽管如果您愿意,您可以这样做。在函数定义的情况下,Lua 使用保留字function开始代码块,然后使用保留字end标记结束。

Lua 拥有现代语言中大多数标准的数据类型,还包括表的概念。在 Lua 中,表是动态创建的对象,可以像传统语言中的指针一样进行操作。表必须在使用前显式创建。表也可以引用其他表,使它们非常适合递归数据类型。操纵表格的通用函数列表包括table.concat.insert.maxn.remove.sort

下面是 Lua 网站上的一小段 Lua 代码,它创建了一个循环链表:

list = {} -- creates an empty table current = list i = 0 while i < 10 do current.value = i current.next = {} current = current.next i = i+1 end current.value = i acurrent.next = list


3

下面是实现 RPC 调用函数的 Lua 代码:

function rpc(client, method, …) assert(method, 'method param is nil') local rpc = { ['id'] = id, ['method'] = method, params = arg } local request = json.encode(rpc) client:send(request .. '\n') id = id + 1 local response = client:receive('*l') local result = json.decode(response) if result.error ~= nil then print(result.error) end return result end

必须的 Lua hello world 脚本:

`require "android"

name = android.getInput("Hello!", "What is your name?") android.printDict(name) -- A convenience method for inspecting dicts (tables). android.makeToast("Hello, " .. name.result)`

Lua wiki 提供了包含大量有用代码片段的样例代码链接。

Perl 5.10.1 版

如果不算 shell,Perl 可能是 SL4A 中最古老的语言。它可以追溯到 1987 年,已经被用于你能想到的几乎所有类型的计算应用中。使用 Perl 的最大优势是可以从中提取大量代码示例。编写hello_world.pl脚本看起来很像其他语言:

use Android; my $a = Android->new(); $a->makeToast("Hello, Android!");

下面是启动 SL4A 脚本所需的 Perl 代码:

`# Given a method and parameters, call the server with JSON,

and return the parsed the response JSON. If the server side

looks to be dead, close the connection and return undef.

sub do_rpc { my self=shift;if(self = shift; if (self->trace) { show_trace(qq[do_rpc: $self: @_]); }`

my $method = pop; my $request = to_json({ id => $self->{id}, method => $method, params => [ @_ ] }); if (defined $self->{conn}) { print { $self->{conn} } $request, "\n"; if ($self->trace) { show_trace(qq[client: sent: "$request"]); } $self->{id}++; my $response = readline($self->{conn}); chomp $response; if ($self->trace) { show_trace(qq[client: rcvd: "$response"]); } if (defined $response && length $response) { my $result = from_json($response); my $success = 0; my $error; if (defined $result) { if (ref $result eq 'HASH') { if (defined $result->{error}) { $error = to_json( { error => $result->{error} } ); } else { $success = 1; } } else { $error = "illegal JSON reply: $result"; } } unless ($success || defined $error) { $error = "unknown JSON error"; } if (defined $error) { printf STDERR "$0: client: error: %s\n", $error; } if ($Opt{trace}) { print STDERR Data::Dumper->Dump([$result], [qw(result)]); } return $result; } } $self->close; return; }

PHP 5.3.3

毫无疑问,PHP 是创建动态网页最成功的通用脚本语言之一。从最初的个人主页,缩写 PHP 现在代表 PHP:超文本预处理器。PHP 是一种免费的开源语言,几乎可以在所有主流操作系统上免费实现。

下面是通过 RPC 启动 SL4A 脚本所需的 PHP 代码:

`public function rpc(method,method, args) { data=array(id=>data = array( 'id'=>this->_id, 'method'=>method,params=>method, 'params'=>args ); request=jsonencode(request = json_encode(data); request.="\n";request .= "\n"; sent = socket_write(this>socket,this->_socket, request, strlen(request));request)); response = socket_read(this->_socket, 1024, PHP_NORMAL_READ) or die("Could not![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) read input\n"); this->_id++; result=jsondecode(result = json_decode(response);

ret=array(id=>ret = array ('id' => result->id, 'result' => result>result,error=>result->result, 'error' => result->error ); return $ret; }`

PHP 版本的hello_world.php看起来是这样的:

<?php require_once("Android.php"); $droid = new Android(); $name = $droid->getInput("Hi!", "What is your name?"); $droid->makeToast('Hello, ' . $name['result']);

在安装 PHP 和基本的hello_world.php时,您会得到许多其他的示例脚本。

犀牛 1.7R2

Rhino 解释器为您提供了一种编写独立 JavaScript 代码的方法。JavaScript 实际上在 ECMA-262 下被标准化为ECMAScript。你可以从[www.ecma-international.org/publication…](http://www.ecma-international.org/publications/standards/Ecma-262.htm)下载标准。拥有一个 JavaScript 解释器的好处有很多。如果您计划使用 HTML 和 JavaScript 构建任何类型的定制用户界面,您可以构建 JavaScript 部分的原型,并使用 Rhino 解释器进行测试。

Rhino 的android.js文件在许多方面与其他语言的文件相似。RPC 调用定义如下所示:

this.rpc = function(method, args) { this.id += 1; var request = JSON.stringify({'id': this.id, 'method': method, 'params': args}); this.output.write(request + '\n'); this.output.flush(); var response = this.input.readLine(); return eval("(" + response + ")"); },

这里有一个简单的 Rhino hello_world.js脚本:

load("/sdcard/sl4a/extras/rhino/android.js"); var droid = new Android(); droid.makeToast("Hello, Android!");

JRuby 1.4

任何开源项目的潜在危险之一是被忽视。在撰写本文时,基于 SL4A r4 的 JRuby 解释器受到了忽视,甚至没有运行hello_world.rb脚本。无论如何,这个脚本看起来是这样的:

require "android" droid = Android.new droid.makeToast "Hello, Android!"

JRuby 解释器确实启动了,您可以用它来尝试一些基本的 JRuby 代码。下面是 Android 类在 Ruby 中的样子:

`class Android

def initialize() @client = TCPSocket.new('localhost', AP_PORT) @id = 0 end

def rpc(method, *args) @id += 1 request = {'id' => @id, 'method' => method, 'params' => args}.to_json() @client.puts request response = @client.gets() return JSON.parse(response) end

def method_missing(method, *args) rpc(method, *args) end

end`

外壳

如果您是一个 shell 脚本向导,那么您将会对 SL4A 的 shell 解释器如鱼得水。它本质上与您在典型的 Linux 终端提示符下看到的 bash 脚本环境相同。你会发现所有熟悉的操作文件的命令,如cplsmkdirmv

Python

Python 有着广泛的用途和大量的追随者,尤其是在 Google 内部。事实上,它的追随者如此之多,以至于他们雇佣了这种语言的发明者吉多·范·罗苏姆。Python 已经存在很长时间了,并且有许多用这种语言编写的开源项目。就 SL4A 而言,它也是最令人感兴趣的,所以你会在论坛上找到比其他语言更多的例子和讨论。出于这个原因,我将花多一点时间来介绍这种语言,试图突出从 SL4A 的角度来看很重要的东西。

语言基础

了解 Python 语言不是这本书的绝对要求,但会有帮助。关于 Python,你需要知道的第一件事是一切都是对象。第二件事是空白在 Python 中是有意义的。我的意思是 Python 使用制表符或实际空格(ASCII 32)而不是花括号来控制代码执行(见图 1-2 )。第三,重要的是要记住 Python 是一种区分大小写的语言。

images

***图 1-2。*Python 中空格用法的例子

在讲授“计算机编程入门”课程时,Python 是一种很好的语言。标准 Python 的每个安装都带有命令行解释器,您可以在其中键入一行代码并立即看到结果。要启动解释器,只需在命令提示符(Windows)或终端窗口(Linux 和 Mac OS X)输入python。此时,您应该看到几行版本信息,后面是三箭头提示(>>>),让您知道您在 Python 解释器中,如下所示:

`C:\Users\paul>python Python 2.6.6 (r266:84297, Aug 24 2010, 18:13:38) [MSC v.1500 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.

`

Python 使用了许多命名约定,如果您仔细研究 Python 代码,就会发现这些约定。第一个是双下划线,它在 Python 中用来“破坏”或更改名称,作为定义类内部使用的私有变量和方法的一种方式。您将看到这种符号用于“特殊”方法,如__init__(self)。如果一个类有特殊的__init__方法,每当该类的新实例化发生时,它就会被调用。

例如:

`>>> class Point: def init(self, x, y): self.x = x self.y = y

xy = Point(1,2) xy.x, xy.y (1, 2)`

从例子中可以看出,self在 Python 中被用作保留字,指的是方法的第一个参数。它实际上是一个 Python 约定,实际上,对 Python 没有特殊的意义。然而,因为这是一个被广泛接受的约定,所以你应该坚持使用它以避免任何潜在的问题。从技术上讲,self是对类或函数本身的引用。一个类中的方法可以通过使用self参数的方法属性来调用同一个类中的其他方法。

Python 有一个简短的内置常量列表。你会遇到的主要有FalseTrueNoneFalseTrue属于bool类型,主要出现在逻辑测试中或者创建一个无限循环。

这种语言的新用户经常感到困惑的事情之一是数据类型的多样性。接下来的部分给出了使用 Python 和 SL4A 所需的关键数据类型的快速概述。

字典:需要唯一键的一组无序的键/值对

Python 字典直接映射到 JSON 数据结构。定义字典的语法使用花括号将条目括起来,并在键和值之间使用冒号。下面是一个简单的字典定义:

students = {'barney' : 1001, 'betty' : 1002, 'fred' : 1003, 'wilma' : 1004}

要引用条目,请使用键,如下所示:

students['barney'] = 999 students['betty'] = 1000

您还可以使用dict()构造函数来构建字典。当键是一个简单的字符串时,您可以使用传递给dict()的参数创建一个新的字典,如下所示:

students = dict(barney=1001, betty=1002, fred=1003, wilma=1004)

因为 Python 中的一切都是一个对象,所以您可以期望看到与 dictionary 对象相关联的方法。如您所料,有一些方法可以从字典中返回键和值。下面是这本students字典的样子:

`>>> students {'barney': 1001, 'betty': 1002, 'fred': 1003, 'wilma': 1004}

students.keys() ['barney', 'betty', 'fred', 'wilma'] students.values() [1001, 1002, 1003, 1004]`

方括号约定表示列表。对students.keys()语句求值会返回来自students字典的键列表。

列表:内置的 Python 序列,类似于其他语言中的数组

在 Python 中,序列被定义为“一个 iterable,它支持通过__getitem__()特殊方法使用整数索引进行有效的元素访问,并定义了一个返回序列长度的len()方法。”一个可迭代被定义为“一个能够一次返回一个成员的容器对象”Python 为迭代提供了直接的语言支持,因为它是编程中最常见的操作之一。列表对象提供了许多方法来简化它们的使用。来自 Python 文档:

  • list.append( x ) :在列表末尾增加一项;相当于a[len(a):] = [x]
  • list.extend( L ) :通过追加给定列表中的所有项目来扩展列表;相当于a[len(a):] = L
  • list.insert( I,x ) :在给定位置插入一个项目。第一个参数是要插入的元素的索引,所以a.insert(0, x)插入到列表的前面,a.insert(len(a), x)相当于a.append(x)
  • list.remove( x ) :删除列表中第一个值为 x 的项目。如果没有此项,则为错误。
  • list.pop(【I】):移除列表中给定位置的项目并返回。如果没有指定索引,a.pop()删除并返回列表中的最后一项。(方法签名中的 i 周围的方括号表示该参数是可选的,并不是说您应该在那个位置键入方括号。)您将在 Python 库参考中经常看到这种符号。
  • list.index( x ) :返回列表中第一项的索引,其值为 x 。如果没有此项,则为错误。
  • list.count( x ) :返回 x 在列表中出现的次数。
  • list.sort :对列表中的项目进行排序,就位。
  • list.reverse( x ) :反转列表中的元素,就位。
字符串:由 ASCII 或 Unicode 字符组成的不可变序列

字符串定义中的关键词是不可变,意思是创建后不可改变。这有助于许多操作的快速实现,也是 Python 能够以非常高效的方式处理字符串的原因。反斜杠()字符用于转义具有特殊含义的字符,包括反斜杠字符本身。如果在字符串前面加上小写或大写的“r”,则不需要反斜杠字符,因为每个字符都将被视为“原始”字符串。您可以使用单引号或双引号来定义字符串文字,如下所示:

name = "Paul Ferrill" initials = 'PF' directory = r'C:\users\paul\documents'

string 类有许多可用的方法。下面是来自 Python 文档的列表: 4

capitalize() center(width[, fillchar]) count(sub[, start[, end]]) decode( [encoding[, errors]]) encode( [encoding[,errors]]) endswith( suffix[, start[, end]]) expandtabs( [tabsize]) find( sub[, start[, end]]) index( sub[, start[, end]]) isalnum( ) isalpha( ) isdigit( ) islower( ) isspace( ) istitle( ) isupper( ) join( seq) ljust( width[, fillchar]) lower( ) lstrip( [chars]) partition( sep) replace( old, new[, count]) rfind( sub [,start [,end]]) rindex( sub[, start[, end]]) rjust( width[, fillchar]) rpartition( sep) rsplit( [sep [,maxsplit]]) rstrip( [chars]) split( [sep [,maxsplit]]) splitlines( [keepends]) startswith( prefix[, start[, end]]) strip( [chars]) swapcase( ) title( ) translate( table[, deletechars]) upper( ) zfill( width)


4

在 Python 2.6 中,内置的strunicode类通过str.format()方法提供了非常全面的格式化和替换功能。还有一个Formatter类,它有一组更广泛的方法,适合实现模板功能。Python 也使用带字符串的百分号进行格式化。以下是在打印语句中使用百分号的示例:

`>>> Pi = 3.141593

print "Pi = %10.2f" % Pi Pi = 3.14 print "Long Pi = %10.6f" % Pi Long Pi = 3.141593`

这是一个谈论在 Python 中使用的好地方。任何有多个条目的对象,比如一个字符串,都可以用一种叫做切片的符号来寻址。为了引用字符串s,中从jk的项目,使用s[j:k].向切片符号添加第三个项目包括一个步骤,以便s[j:k:l]引用从jk的项目,增量为l。从列表的开始到第 n 项,使用s[:n],从第 n 项到末尾使用s[n:]。例如:

`>>> mylist = [1,2,3,4,5,6,7,8,9]

mylist[:3] [1, 2, 3] mylist[3:] [4, 5, 6, 7, 8, 9]`

注意 Python 使用从零开始的引用。因此,mylist 的第一个或第零个元素将等于 1。语法mylist[:3]说“返回 mylist 的所有元素,从开始到第四个元素,但不包括第四个元素。”负索引从列表末尾开始计数。

元组:一个不可变的列表

与字符串类型一样,元组是不可变的,这意味着一旦创建就不能更改。元组使用与列表相同的语法来定义,只是它们用圆括号而不是方括号括起来。下面是一个元组的例子:

`>>> Sentence = ("Today", "is", "the", "first", "day", "of", "the", "rest", "of", "your",images "life.")

Sentence ('Today', 'is', 'the', 'first', 'day', 'of', 'the', 'rest', 'of', 'your', 'life.') Sentence[0] 'Today' Sentence[1:3] ('is', 'the') Sentence[-1] 'life.'`

Python 标准库

Python 语言的最大优势之一是 Python 标准库,它包含了各种各样的例程,使您的编码生活更加简单。尽管本简介没有足够的篇幅来介绍整个库,但我将尝试指出一些将在后面章节中出现的关键函数。

Python 语言的所有文档都可以在[docs.python.org](http://docs.python.org)找到,包括旧版本。SL4A 使用 Python 2.6,所以您需要查看旧版本以获得正确的信息。对于 Python 标准库,您将希望从[docs.python.org/release/2.6.5/library/index.html](http://docs.python.org/release/2.6.5/library/index.html)开始。

Python 解释器有大量的内置函数,这些函数总是可用的。其中一个函数是dir(),它显示一个模块定义的所有名字。如果您在 Python 提示符下键入dir(),您将收到当前本地范围内的名称列表,如下所示:

`>>> dir() ['builtins', 'doc', 'name', 'package']

import sys dir() ['builtins', 'doc', 'name', 'package', 'sys'] import os dir() ['builtins', 'doc', 'name', 'package', 'os', 'sys']`

如果您向dir()传递一个参数,您将获得该对象所有属性的列表。Python 标准库中的许多模块都实现了一个名为__dir__()的方法,这个方法在被调用时实际上会产生属性列表。如果没有__dir__()方法,该函数将尝试使用对象的__dir__()属性来获取它需要的信息。

您可以在任何对象上使用dir()来检查模块的属性。以下是您在 android 对象上使用dir()时得到的结果:

`>>> import android

dir(android) 'Android', 'HANDSHAKE', 'HOST', 'PORT', 'Result', 'author', 'builtins', 'doc',![images 'file', 'name', 'package', 'collections', 'json', 'os', 'socket', 'sys']`

另一个例子是定义一个字符串来查看有哪些方法可用:

`>>> a = "Hello"

dir(a) 'add', 'class', 'contains', 'delattr', 'doc', 'eq', 'format',![images'ge', 'getattribute', 'getitem', 'getnewargs', 'getslice', 'gt',images 'hash', 'init', 'le', 'len', 'lt', 'mod', 'mul', 'ne',images 'new', 'reduce', 'reduce_ex', 'repr', 'rmod', 'rmul',images 'setattr', 'sizeof', 'str', 'subclasshook', '_formatter_field_name_split',images '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith',images 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower',images 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition',images 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split',images 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']`

有时候,您可能正在使用 Python 标准库,甚至是其他库,但您不知道返回变量的类型。为此,您可以像这样使用type()内置函数:

>>> type(a) <type 'str'>

数字转换是经常需要的函数之一,Python 在这方面做得很好。这里有几个例子:

`>>> bin(12345678) '0b101111000110000101001110'

hex(12345678) '0xbc614e' oct(12345678) '057060516'`

这些函数的结果是一个字符串。要将字符串转换成整数,使用int()函数如下:

>>> print int('0xbc614e',16) 12345678

Python 中的文件输入和输出使用非常简单的方法。要打开一个文件,使用open,如图 1-3 中的所示。

images

***图 1-3。*Python 文件打开的例子

支持的文件模式包括追加(' a ')、读取(' r ')和写入(' w ')。open返回一个类型为file的对象,包含所有选择的方法和属性。看起来是这样的:

`>>> infile = open(r'c:\users\paul\documents\dependents.txt')

type(infile) <type 'file'> dir(infile) 'class', 'delattr', 'doc', 'enter', 'exit', 'format',![images'getattribute', 'hash', 'init', 'iter', 'new', 'reduce',images 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook',images 'close', 'closed', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'mode', 'name',images 'newlines', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace',v 'tell', 'truncate', 'write', 'writelines', 'xreadlines']`

os模块为底层操作系统提供独立于平台的接口。如果你打算对文件名做些什么,你会想了解一下os模块。如果你导入操作系统,然后执行dir(os):,你会得到以下结果

>>> dir(os) 'F_OK', 'O_APPEND', 'O_BINARY', 'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY',![images'O_RDWR', 'O_SEQUENTIAL', 'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'P_DETACH', 'P_NOWAIT', 'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'R_OK', 'SEEK_CUR', 'SEEK_END',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'SEEK_SET', 'TMP_MAX', 'UserDict', 'W_OK', 'X_OK', '_Environ', '__all__', '__builtins__',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) '__doc__', '__file__', '__name__', '__package__', '_copy_reg', '_execvpe', '_exists',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) '_exit', '_get_exports_list', '_make_stat_result', '_make_statvfs_result',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) '_pickle_stat_result', '_pickle_statvfs_result', 'abort', 'access', 'altsep', 'chdir',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'chmod', 'close', 'closerange', 'curdir', 'defpath', 'devnull', 'dup', 'dup2', 'environ',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'errno', 'error', 'execl', 'execle', 'execlp', 'execlpe', 'execv', 'execve', 'execvp',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'execvpe', 'extsep', 'fdopen', 'fstat', 'fsync', 'getcwd', 'getcwdu', 'getenv', 'getpid',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'isatty', 'linesep', 'listdir', 'lseek', 'lstat', 'makedirs', 'mkdir', 'name', 'open',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'pardir', 'path', 'pathsep', 'pipe', 'popen', 'popen2', 'popen3', 'popen4', 'putenv',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'read', 'remove', 'removedirs', 'rename', 'renames', 'rmdir', 'sep', 'spawnl', 'spawnle',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'spawnv', 'spawnve', 'startfile', 'stat', 'stat_float_times', 'stat_result',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'statvfs_result', 'strerror', 'sys', 'system', 'tempnam', 'times', 'tmpfile', 'tmpnam',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'umask', 'unlink', 'unsetenv', 'urandom', 'utime', 'waitpid', 'walk', 'write']

如您所见,有相当多的方法可用。处理文件和目录的另一个方便的模块是glob,您可以使用它根据通配符获得特定目录中的文件列表,如下所示:

>>> import glob for file in glob.glob("*.jpg"): print file

这将打印当前工作目录中所有.jpg文件的列表。命中列表上的下一个模块是datetime。如果你需要处理日期或时间,你将需要datetime。以下是使用该模块的几个示例:

`>>> print datetime.date.today() 2011-04-26

print datetime.datetime.now() 2011-04-26 14:35:25.045000 print datetime.date.weekday(datetime.datetime.now()) 1`

文件工具列表的最后一项是shutil。该模块提供了许多文件工具,如shutil.copyshutil.copytreeshutil.move,shutil.rmtree。如果您导入shutil并使用dir()查看这些方法,您会得到以下结果:

`>>> import shutil

dir(shutil) 'Error', 'all', 'builtins', 'doc', 'file', 'name', 'package',![images'_basename', '_samefile', 'abspath', 'copy', 'copy2', 'copyfile', 'copyfileobj',images 'copymode', 'copystat', 'copytree', 'destinsrc', 'fnmatch', 'ignore_patterns', 'move',images 'os', 'rmtree', 'stat', 'sys']`

处理存储在逗号分隔值(CSV)文件中的数据是另一项常见任务。Python 2.6 包含了用于这种任务的csv模块。下面是一个简单的脚本,它使用csv模块简单地打印出文件中的所有行:

import csv reader = csv.reader(open("some.csv", "rb")) for row in reader: print row

您也可以用类似的方式写入 CSV 文件:

import csv writer = csv.writer(open("some.csv", "wb")) writer.writerows(someiterable)

此示例显示了如何将数据解析为列表中的单独项目:

`>>> import csv

for row in csv.reader(['one,two,three']): print row

['one', 'two', 'three']`

如果不知道文件中有多少列,可以使用string.count方法,如下所示:

`>>> import string

string.count('one,two,three,four,five',',') 4`

我们要看的来自csv模块的最后一个方法是DictReader.在大多数情况下,您应该知道 CSV 文件中包含哪些字段。如果确实如此,您可以使用 DictReader 函数将文件读入字典。下面是一个包含姓名、地址和电话号码的示例文本文件:

John Doe|Anycity|ST|12345|(800) 555-1212 Jane Doe|Anycity|ST|12345|(800) 555-1234 Fred Flinstone|Bedrock|ZZ|98765|(800) 555-4321 Wilma Flinstone|Bedrock|ZZ|98765|(800) 555-4321 Bambam Flinston|City|ST|12345|(800) 555-4321 Barney Rubble|Bedrock|ZZ|98765|(800) 555-1111

我们的示例代码还需要一个模块:itertools。这个模块提供了创建 Python 使用的高效迭代器的函数,带有关键字for。读取文件并打印结果的代码如下所示:

`import itertools import csv

HeaderFields = ["Name", "City", "State", "Zip", "PhoneNum"]

infile = open("testdata.txt")

contacts = csv.DictReader(infile, HeaderFields, delimiter="|")

for header in itertools.izip(contacts): print "Header (%d fields): %s" % (len(header), header)`

最后,输出如下:

Header (1 fields): ({'City': 'Anycity', 'State': 'ST', 'PhoneNum': '(800) 555-1212', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'John Doe', 'Zip': '12345'},) Header (1 fields): ({'City': 'Anycity', 'State': 'ST', 'PhoneNum': '(800) 555-1234', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Jane Doe', 'Zip': '12345'},) Header (1 fields): ({'City': 'Bedrock', 'State': 'ZZ', 'PhoneNum': '(800) 555-4321', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Fred Flinstone', 'Zip': '98765'},) Header (1 fields): ({'City': 'Bedrock', 'State': 'ZZ', 'PhoneNum': '(800) 555-4321', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Wilma Flinstone', 'Zip': '98765'},) Header (1 fields): ({'City': 'City', 'State': 'ST', 'PhoneNum': '(800) 555-4321', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Bambam Flinston', 'Zip': '12345'},) Header (1 fields): ({'City': 'Bedrock', 'State': 'ZZ', 'PhoneNum': '(800) 555-1111', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Barney Rubble', 'Zip': '98765'},) Header (1 fields): ({'City': 'Bedrock', 'State': 'ZZ', 'PhoneNum': '(800) 555-1111', 'Name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'Betty Rubble', 'Zip': '98765'},)

weekday方法返回周一的0,周二的1,依此类推。您以后可能需要的一项功能是将系统时间戳值转换为人类可读的字符串。下面是在后面章节中使用的一段代码,它将 SMS 消息的时间戳转换成一个字符串:

b = '' for m in SMSmsgs: millis = int(message['date'])/1000 strtime = datetime.datetime.fromtimestamp(millis) b += strtime.strftime("%m/%d/%y %H:%M:%S") + ',' + m['address'] + ',' + m['body'] + '\n'

为移动设备编写代码将不可避免地涉及到以某种方式与网站进行通信。Python 标准库有两个模块来帮助完成这项任务:urlliburllib2。尽管这两个模块提供了相似的功能,但它们以不同的方式实现。当您导入两者并检查它们的方法时,您会得到以下结果:

`>>> import urllib

dir(urllib) 'ContentTooShortError', 'FancyURLopener', 'MAXFTPCACHE', 'URLopener', 'all',![images'builtins', 'doc', 'file', 'name', 'package', 'version',images '_ftperrors', '_have_ssl', '_hextochr', '_hostprog', '_is_unicode', '_localhost',images '_noheaders', '_nportprog', '_passwdprog', '_portprog', '_queryprog', '_safemaps',images '_tagprog', '_thishost', '_typeprog', '_urlopener', '_userprog', '_valueprog', 'addbase',images 'addclosehook', 'addinfo', 'addinfourl', 'always_safe', 'basejoin', 'ftpcache',images 'ftperrors', 'ftpwrapper', 'getproxies', 'getproxies_environment', 'getproxies_registry',images 'localhost', 'main', 'noheaders', 'os', 'pathname2url', 'proxy_bypass',images 'proxy_bypass_environment', 'proxy_bypass_registry', 'quote', 'quote_plus', 'reporthook',images 'socket', 'splitattr', 'splithost', 'splitnport', 'splitpasswd', 'splitport', 'splitquery',images 'splittag', 'splittype', 'splituser', 'splitvalue', 'ssl', 'string', 'sys', 'test',images 'test1', 'thishost', 'time', 'toBytes', 'unquote', 'unquote_plus', 'unwrap',images 'url2pathname', 'urlcleanup', 'urlencode', 'urlopen', 'urlretrieve', 'warnings'] import urllib2 dir(urllib2)`

'AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler',![images'BaseHandler', 'CacheFTPHandler', 'FTPHandler', 'FileHandler', 'HTTPBasicAuthHandler',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'HTTPCookieProcessor', 'HTTPDefaultErrorHandler', 'HTTPDigestAuthHandler', 'HTTPError',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'HTTPErrorProcessor', 'HTTPHandler', 'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'HTTPRedirectHandler', 'HTTPSHandler', 'OpenerDirector', 'ProxyBasicAuthHandler',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'ProxyDigestAuthHandler', 'ProxyHandler', 'Request', 'StringIO', 'URLError',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'UnknownHandler', '__builtins__', '__doc__', '__file__', '__name__', '__package__',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) '__version__', '_cut_port_re', '_opener', '_parse_proxy', 'addinfourl', 'base64', 'bisect',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'build_opener', 'ftpwrapper', 'getproxies', 'hashlib', 'httplib', 'install_opener',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'localhost', 'mimetools', 'os', 'parse_http_list', 'parse_keqv_list', 'posixpath',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'proxy_bypass', 'quote', 'random', 'randombytes', 're', 'request_host', 'socket',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'splitattr', 'splithost', 'splitpasswd', 'splitport', 'splittype', 'splituser',![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'splitvalue', 'sys', 'time', 'unquote', 'unwrap', 'url2pathname', 'urlopen', 'urlparse']

如果你只是想下载一个 URL 的内容,就像你在台式机上右击并另存为一样,你可以使用urllib.urlretrieve。在urllib中有许多构建或解码 URL 的辅助方法,包括pathname2urlurl2pathnameurlencodequoteunquote。下面是urllib的一段代码:

`import urllib

class OpenMyURL(urllib.FancyURLopener):

read a URL with HTTP authentication

def setpasswd(self, user, passwd): self.__user = user self.__passwd = passwd

def prompt_user_passwd(self, host, realm): return self.__user, self.__passwd

urlopener = OpenMyURL() urlopener.setpasswd("user", "password")

f = urlopener.open("www.aprivatesite.com") print f.read()`

如果你需要通过 FTP 下载文件,你会想要使用urllib2。这是我在stackoverflow.com上找到的一个例子:

`>>> files = urllib2.urlopen('ftp://ftp2.census.gov/geo/tiger/TIGER2008/01_ALABAMA/')images .read().splitlines()

for l in files[:4]: print l … drwxrwsr-x 2 0 4009 4096 Nov 26 2008 01001_Autauga_County drwxrwsr-x 2 0 4009 4096 Nov 26 2008 01003_Baldwin_County drwxrwsr-x 2 0 4009 4096 Nov 26 2008 01005_Barbour_County drwxrwsr-x 2 0 4009 4096 Nov 26 2008 01007_Bibb_County`

为了实现一个基本的 HTTP 服务器,有一个简单的 HTTP server。以下是您得到的结果:

`>>> import SimpleHTTPServer

dir(SimpleHTTPServer) 'BaseHTTPServer', 'SimpleHTTPRequestHandler', 'StringIO', 'all', 'builtins',![images'doc', 'file', 'name', 'package', 'version', 'cgi', 'mimetypes',images 'os', 'posixpath', 'shutil', 'test', 'urllib']`

我将在第七章中使用这个模块来构建一个快速脚本,让你从网络浏览器访问你的 Android 设备上的任何目录。您可能需要本地 IP 地址的值。套接字库在这里起了作用。获得自己的地址的一个技巧是连接到一个众所周知的网站,并从连接细节中检索您的地址。下面是如何做到这一点:

`>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.connect(("gmail.com",80)) print s.getsockname() ('192.168.1.8', 64300)`

有一些函数返回代表 IP 地址的十进制或十六进制数。套接字库提供了两个函数来帮助你从一个十进制数字转换到标准 IP 地址符号中的四个数字,反之亦然。您还需要 Python struct模块。代码如下:

`>>> import socket, struct

struct.unpack('L', socket.inet_aton('192.168.1.8'))[0] 134326464 socket.inet_ntoa(struct.pack('L',134326464)) '192.168.1.8'`

要从 Python 访问任何 Android API 函数,您必须import android然后像这样实例化一个对象:

`>>> import android

droid = android.Android()`

完成后,您就可以完全访问所有 API 函数了。下面是从android.py开始的 Android 类的样子:

`class Android(object):

def init(self, addr=None): if addr is None: addr = HOST, PORT self.conn = socket.create_connection(addr) self.client = self.conn.makefile() self.id = 0 if HANDSHAKE is not None: self._authenticate(HANDSHAKE)

def _rpc(self, method, *args): data = {'id': self.id, 'method': method, 'params': args} request = json.dumps(data) self.client.write(request+'\n') self.client.flush() response = self.client.readline() self.id += 1 result = json.loads(response) if result['error'] is not None: print result['error']

namedtuple doesn't work with unicode keys.

return Result(id=result['id'], result=result['result'], error=result['error'], )

def getattr(self, name): def rpc_call(*args): return self._rpc(name, *args) return rpc_call`

下面是 SL4A 安装中的一个简单 Python hello_world.py脚本:

`>>> import android

droid = android.Android() name = droid.getInput("Hello!", "What is your name?") print name Result(id=0, result=u'Paul', error=None) droid.makeToast("Hello, %s" % name.result) Result(id=1, result=None, error=None) `

当您将 Python 语言的简单性与 Python 标准库的广度相结合时,您将获得一个在桌面、服务器或 Android 设备上使用 SL4A 实现有用脚本的强大工具。

图 1-4 显示了当你运行简单的hello_world.py脚本时,你在屏幕上看到的第一个东西。当你像我一样输入你的名字并按下 Ok 按钮时,你会看到如图图 1-5 所示的弹出信息,这是调用makeToast API 的结果。

images

图 1-4。 Hello World 输入对话框

images

***图 1-5。*make toast API 调用的结果

总结

本章从一般意义上介绍 Android 平台,更具体地介绍 SL4A。在你继续阅读本书的其余部分时,这里有一些事情值得注意和记住。

这是你应该从这一章中抓住的:

  • Linux 基础:Android 操作系统本质上是幕后的 Linux。许多相同的目录和程序都在那里。
  • Java 是 Android 的语言:基于 Android SDK 构建的应用运行在 Java DVM 中。虽然这不是绝对必要的,但了解一点 Java 有助于理解 SL4A 背后的一些管道,这没有什么坏处。
  • SL4A 是一个容器应用:它托管所有不同的语言解释器,并使用 RPC 向底层操作系统提供接口。
  • JSON 是传递数据的方式:格式良好的 JSON 不难阅读,事实上,它有非常逻辑的结构。一旦您习惯了语法,您应该能够处理任何 API 函数。
  • 语言选项:虽然 Python 是最流行的,但是您也可以选择其他语言,包括 Beanshell、JRuby、Lua、Perl、PHP 和 Rhino。

二、入门指南

这一章将会给你所有你需要的关于谷歌 Android 脚本层(SL4A)的信息。

images 本章将介绍一些主题,稍后会有更详细的介绍。

好吧。我们开始吧。以下是我将在本章中介绍的内容:

  • 在您的设备上安装核心文件
  • 安装 Android SDK
  • 远程连接到您的设备
  • 执行简单的程序

通过使用这里给出的说明,您将能够在很短的时间内开始使用 SL4A。请密切注意有关配置您的设备和桌面的章节,因为需要完成这些说明才能使两者进行通信。当我带你浏览这些例子时,你会发现跟随它们是有帮助的。

在设备上安装 SL4A

开始使用 SL4A 的最快方法是简单地将其安装在 Android 设备上。有几种方法可以做到这一点。如果您导航到 SL4A 主页(http:// code.google.com/p/android-scripting,您将找到的下载链接。apk 文件和二维码,用于安装在您的设备上的条形码扫描仪。下面列出了安装 SL4A 需要完成的步骤:

  1. Download the SL4A .apk file (see Figure 2-1).images

    ***图 2-1。*下载。apk 文件

  2. Launch the .apk file from the notifications screen (see Figure 2-2).images

    ***图 2-2。*下水了。apk 文件

  3. Select Install on the next screen to actually install SL4A (see Figure 2-3).images

    ***图 2-3。*安装 SL4A

不要担心这些警告,因为它们只是表明 SL4A 有能力执行这些任务中的任何一项,但是除非您实际编写一个脚本来执行这些任务,否则不会这样做。如果您选择在模拟器中安装,只需从模拟器中的浏览器导航到 SL4A 网站,然后单击 QR 码或下载下的sl4a_rx.apk链接。

首次启动 SL4A 应用时,系统会询问您是否允许收集匿名使用信息。无论哪种方式,您都可以通过首选项菜单随时改变主意。

现在您已经安装了 SL4A 主应用,您仍然需要安装您最喜欢的解释器。这可以通过启动 SL4A 应用,然后按下菜单按钮来完成(参见图 2-4 )。

images

图 2-4。 SL4A 菜单按钮弹出

这将在屏幕底部显示许多按钮,包括一个带标签的视图。触摸该按钮会弹出一个选择对话框,包括解释器选项(见图 2-5 )。

images 注意因为这本书主要是关于使用触摸作为主要用户交互的 Android 设备,所以你会看到经常使用的词语触摸选择。还会提到其他基于手指的动作,如向下拖动或从左向右滑动。

images

图 2-5。 SL4A 查看按钮弹出

选择解释器选项会将你带到一个默认屏幕,该屏幕只列出 Shell 作为可用选项。要安装额外的解释器,再次按下菜单按钮,然后触摸添加按钮(参见图 2-6 )。

images

***图 2-6。*口译员屏幕选项菜单

这将显示一个可供下载和安装的解释器列表(见图 2-8 )。选择 Python 2.6.2 这样的版本将会从 SL4A 主网站下载主要的 Python 解释器包。你必须再次访问通知栏,从显示屏顶部向下滑动,通过触摸文件名启动 Python 安装程序(见图 2-7 )。

images 注意在这一点上,花点时间查看解释器屏幕菜单选项中的终端帮助是值得的。在口译员屏幕可见的情况下,按硬件菜单按钮,然后选择帮助。在那里,选择终端帮助并阅读关于输入和编辑文本。

images

图 2-7。 Python 解释器下载通知

images

图 2-8。 SL4A 增加一个翻译

在这一点上,我应该提到还有一种安装 Python 解释器的替代方法。这种方法包括下载.apk文件,然后使用 Android 调试桥(ADB)安装它。如果您希望尝试任何新的 Python for Android 版本,您将需要使用这种方法,因为 SL4A 的基础安装通常指向最新的正式发布版本。在这种情况下,您可以使用 web 浏览器导航到 Google code 站点([code.google.com/p/python-for-android](http://code.google.com/p/python-for-android)),然后使用右键单击并另存为的方法下载PythonForAndroid_rx.apk文件,其中 x 代表您想要测试的版本。接下来,您将使用以下 ADB 命令将.apk文件安装到仿真器或物理设备上:

adb install PythonForAndroid_r6.apk

触摸安装按钮开始实际安装,并显示与 SL4A 安装完成时相同的“打开”和“完成”按钮。使用早期版本的 Python 解释器,你会在触摸“打开”后看到一个安装按钮(见图 2-9 )。用于 Android 的 Python 的最新版本将呈现一个类似于图 2-10 中的屏幕。添加了三个新按钮以方便模块管理。我所说的模块是指普通 Python 发行版中不包含的额外库模块。Python for Android 项目提供了几个这样的模块,当你点击“浏览模块”按钮时,你可以看到它们。这将在 Python for Android wiki 网站上打开一个网页,让您有机会下载它们(参见图 2-11 )。

images

***图 2-9。*为 Android 安装 Python

images

图 2-10。 Python for Android 安装程序

如果一切运行正常,您应该会看到一个快速弹出的对话框,上面写着安装成功。此时,将出现一个屏幕,其中列出了 Shell 和 Python 2.6.2 解释器。可以以类似的方式添加额外的解释器。选择 Python 2.6.2 选项将启动 Python 解释器。

images

图 2-11。 Python-for-android 模块页面

现在你终于可以在标准的 Python 命令提示符下输入代码了,如图 2-13 所示。您可以输入任何有效的 Python 代码,并立即看到结果。如果您想要访问任何 android 功能,您必须导入 Android 模块。实现典型的“Hello World”程序总共需要三行代码,如下所示:

`>>> import android

droid = android.Android() droid.makeToast('Hello, Android World')`

当你在第三行之后点击回车键,你应该会看到一个弹出的对话框,里面有文字“你好,安卓世界”(见图 2-12 )。几秒钟后,对话框将自动关闭。

images

***图 2-12。*make toast 函数调用结果

images

图 2-13。 SL4A Python 解释器提示

如果您在解释器中按下菜单按钮,您会在屏幕底部看到四个按钮,分别标记为“强制大小”、“电子邮件”、“首选项”和“退出并编辑”。这些按钮对于每个解释器都是通用的,因此您可以从 Python、BeanShell 或您选择安装的任何其他解释器中访问它们。图 2-14 显示了这些按钮的样子。

images 注意所有脚本都存储在您设备的 SD 卡上的/sdcard/sl4a/scripts 目录下。如果您使用 USB 电缆将设备连接到主机,SD 卡将不可用,您的脚本也不可见。

images

图 2-14 。SL4A Python 解释器菜单

力度按钮允许你改变解释器的屏幕尺寸。默认值为 80 × 25,这非常适合横向模式下的屏幕。一旦你选择了你的尺寸并选择了调整尺寸按钮,你的屏幕将会调整到新的尺寸。图 2-15 显示了调整大小对话框。

images

图 2-15。 SL4A 解释器屏幕调整对话框

电子邮件菜单选项将捕获解释器屏幕中的所有文本,并将其加载到电子邮件中,允许您将键入的所有内容发送给自己(或任何人)。下面是前面的“Hello World”代码的电子邮件文本,为了清楚起见,对实际消息进行了一些编辑,添加了回车:

`Python 2.6.2 (r262:71600, Sep 19 2009, 11:03:28) [GCC 4.2.1] on linux2 Type "help", "copyright", "credits" or "license" for more information.

import android droid = android.Android() droid.makeToast('Hello, Android World') Result(id=1, result=None, error=None) `

这是一个停下来谈论在你的主机和设备之间移动文件的好时机。最简单的方法是使用 USB 电缆连接您的设备,并将连接类型设置为磁盘驱动器。有了这个设置,您应该能够在任何操作系统(OS)上使用普通的文件管理器应用浏览设备上的文件。此时,在主机和设备之间移动文件变成了简单的拖放操作。在后面的章节中,当我们更详细地讨论 Android SDK 时,我们将会看到一些其他的方法。

现在回到解释器选项菜单。对于 SL4A R3,“退出和编辑”按钮是灰色的,这意味着它当前没有实现。选择 Preferences 按钮会显示一个新的可滚动页面,其中有多个条目,按功能区域分组,允许您配置任意数量的不同选项。这些选项中的一些,例如字体大小,在不同的标题下重复,使得在编辑器工具和解释器窗口中改变字体大小成为可能。

有几个选项你应该注意。如果在第一次安装 SL4A 时选择了允许使用情况跟踪,则可以通过首选项屏幕上的第一个条目进行更改。图 2-16 显示了首选项屏幕的前四个选项,包括一般使用跟踪、脚本管理器显示所有文件、脚本编辑器字体大小和终端滚动大小。其中大多数都是不言自明的,但是我将强调一些有趣的东西。

images

***图 2-16。*SL4A 偏好菜单的第一部分

启用脚本管理器标题下的显示所有文件选项将显示设备上/sdcard/sl4a/scripts目录中的所有文件。如果您使用其他文件作为应用的一部分,并且希望验证它们确实位于正确的目录中,这将非常方便。脚本编辑器标题下的字体大小选项将仅为文本编辑器设置字体大小。在终端标题下还有另一个字体大小选项,当你打开解释器命令提示符时,它将设置终端窗口中字符的大小。

终端标题还有其他一些有趣的设置。旋转模式可让您选取终端窗口可见时屏幕的行为方式。选项包括默认、强制横向、强制纵向和自动。您可能希望终端窗口总是以横向模式打开,这种模式更容易看到,但是您可以使用的屏幕空间有限。当您旋转设备时,自动选项将为您旋转屏幕。

“终端”窗口的默认屏幕颜色是黑色背景上的白色文本。您可以使用色轮选择器或滑块控件将它们更改为您喜欢的任何形状。出于截屏的目的,我设置了与默认颜色完全相反的颜色,即白色背景上的黑色文本。“颜色选择器”对话框显示当前颜色的 HSV、RGB 和 YUB 值,以及可用于 CSS 的十六进制代码。图 2-17 显示了颜色选择器的样子。您可以通过选择“接受”按钮来接受您的更改,或者使用“恢复”按钮恢复到之前的状态。

images

***图 2-17。*终端窗口颜色选择器

您可能还想启用一个终端标题选项,那就是保持屏幕清醒选项。默认情况下它应该是打开的,但是如果当你正在思考下一行代码时,你的屏幕开始消失,你将知道该寻找哪个选项。接下来,我将带您在开发机器上安装 Android SDK,以利用那里的一些工具。

安装 Android SDK

Android SDK 提供了许多工具,让开发人员的工作变得更加轻松。我将在这里解决安装 SDK 的问题,并在后面的章节中深入探讨它的内容。第一步是下载适合您的操作系统的安装文件。谷歌为 Linux、Mac OS X 和 Windows 提供安装程序。如果您的机器上还没有安装 Java 开发工具包(JDK ),您还需要先安装它。我将解决所有三个平台上的安装问题,以确保基础都包括在内。

Linux

为了在 Linux 上安装 SDK,我将从全新安装 64 位版本的 Ubuntu 10.10 桌面开始。在继续操作之前,您还需要确保系统安装了最新的安全补丁。您必须安装的第一件事是基础 Java 包。这可以通过终端窗口中的以下命令来实现:

$ sudo add-apt-repository ppa:sun-java-community-team/sun-java6 $ sudo apt-get update $ sudo apt-get install sun-java6-jre sun-java6-bin sun-java6-jdk

为了让它正常工作,您需要执行另一个命令行安装来让 SDK 完全正常工作。这与 SDK 使用 32 位包有关。创建一个新的 Android 虚拟设备(AVD)使用了mksdcard工具,它依赖于 ia32-libs 包。要安装此依赖项,您需要在终端窗口中输入另一个命令:

$ sudo apt-get install ia32-libs

对于 Linux,在 Android SDK 下载页面上有一个包含所有基础 SDK 文件的.tgz文件([developer.android.com/sdk](http://developer.android.com/sdk))。如果你的浏览器使用 Firefox,你应该可以选择使用 File Roller 工具打开文件。将整个目录解压到某个方便的地方。一旦您提取了文件,您需要运行 SDK 管理器应用来实际安装一个或多个版本的 Android 平台。

SDK Manager 应用在所有三个平台上看起来几乎是一样的,所以我将只描述一次下载特定版本的过程。在 Linux 上,管理器应用被命名为android,存在于主 SDK 根目录下的tools子目录中。您可以通过双击android文件从 Nautilus 文件管理器窗口启动它。如果您真的很好奇,可以在文本编辑器中打开该文件,因为它实际上是一个将启动sdkmanager.jar的 shell 脚本。这将调出 SDK 管理器主屏幕,如图图 2-18 所示。

images

图 2-18。 Android SDK 管理器屏幕

images 注意如果您通过代理访问互联网,您需要在设置菜单中添加该信息,如图图 2-19 所示。

images

***图 2-19。*代理服务器设置页面

选择安装哪个版本主要取决于您将支持的设备上运行的 Android 版本。对于本书中的示例,我将安装所有 2.x 版本和示例。为此,您只需单击这些项目旁边的复选框,以及 Android SDK 平台工具和文档条目,然后单击 Install Selected 按钮。如果有相当快的网络连接,整个过程应该不到十分钟。

在桌面上创建一些由 SDK 安装的常用程序的快捷方式以备后用是有意义的。你可以用 GNOME 右键点击桌面并选择创建启动器。该对话框提示在图标下显示名称以及要执行的命令。如果您单击命令文本框旁边的浏览按钮,您将能够导航到 SDK 工具目录。点击android文件,将其设置为新启动器的执行目标。现在,您可以通过桌面上的图标快速访问 SDK Manager 应用。

SDK 安装说明建议您也将toolsplatform-tools目录添加到您的路径中。如果您将 SDK 文件解压缩到您的主目录中,您可以从命令行使用以下命令进行设置:

export PATH=${PATH}:~/android-sdk-linux-x86/tools:~/android-sdk-linux-x86/platform-tools

images 注意注意 SDK 版本之间的细微变化,这些变化可能会让你陷入困境。谷歌从 SDK 版本 8 到 9 (Android 2.2 到 2.3)改变了惯例,包括几个最常用的工具的目录。

Mac OS X

在 Mac 上安装包括下载一个 zip 文件,然后解压其中的内容。您必须运行 Android SDK 和 AVD 管理器应用,然后选择要安装的版本。要开始这个过程,您可以从 Finder 启动 Android 应用,或者从您解压缩 SDK 下载的目录中的终端窗口使用以下命令启动:

$ tools/android

第一次运行 SDK Manager 应用时,您将看到一个可供选择的 SDK 版本列表。从命令行的角度来看,OS X 看起来几乎和 Linux 一样,包括设置路径语句。您可以使用与以前相同的语法,注意指定 SDK 目录的正确路径。在我的例子中,我在主目录的顶层解包了 SDK,并使用了以下导出语句:

export PATH=${PATH}:~/android-sdk-mac-x86/tools:~/android-sdk-linux-x86/platform-tools

您需要将这一行添加到。bash_profile 文件,以便在下次登录时修改路径。您可能想要在 dock 上创建一个快捷方式来快速启动 SDK 管理器。你所要做的就是把android文件从tools目录拖到 dock 的右边。现在,您可以一键访问 SDK 管理器,并从那里启动设备仿真器。我们将在第三章中深入探讨如何使用设备模拟器。

Windows

Google 提供了一个 zip 文件和一个可执行文件来在 Windows 上安装 SDK。下载文件的大小几乎是一样的,所以选择一个不应该有任何区别。如果您下载了。exe 文件,您就少了一个解压文件的步骤。这也是推荐的选择,所以这就是我要说的。对于所有的 Windows 开发和示例,我将使用 64 位版本的 Windows 7 旗舰版。要开始这个过程,我只需点击 Android SDK 下载页面上的installer_r08-windows.exe链接。一旦文件下载完毕,你需要双击来启动安装程序。如果你没有 Java 开发工具包(JDK),你会看到如图图 2-20 所示的屏幕。

images Android SDK。exe 安装程序会查找 32 位版本的 JDK,如果您没有安装它,安装程序将不会继续。

images

图 2-20 。缺少 JDK 屏幕

尽管我在 64 位 Windows 上进行测试,但我发现必须安装 32 位 JDK 才能安装 Android SDK。一旦你下载了 JDK,你只需双击文件名开始安装。完成该步骤后,您现在可以继续安装 Android SDK 了。安装完成后,您将可以选择启动 SDK 管理器。在 Windows 上首次启动 SDK 管理器看起来与 Linux 和 Mac OS X 略有不同。图 2-21 显示了您将看到的对话框,允许您选择要安装的特定软件包。

您会注意到用于选择 SDK 版本的对话框略有不同,它要求您在对话框中选择一个特定的行,然后单击 Reject 单选按钮取消选择一个版本。因为我将只使用 SDK 的 2.x 版本,所以我取消了 1.5 和 1.6 版本选项。完成后,我点击了安装按钮,然后一切运行,没有干预。当你选择.exe安装程序时,你会得到一个新的选项添加到你的 Windows 程序菜单,标签为 Android SDK 工具。您也可以右键单击 SDK 管理器图标,并将其拖到桌面上以便快速访问。请注意,如果您只是简单地从 Windows 程序菜单中拖动图标,实际上您会将它移动到桌面上。

images

***图 2-21。*初始 Android SDK 管理器屏幕

安装 Python

虽然你可以直接在你的设备上输入代码,但你会很快发现这个过程非常乏味,除非你碰巧有一个全尺寸的键盘。从主机远程连接为开发和测试应用提供了一个更高效的环境。我将在第四章中介绍如何使用 Eclipse 来实现这一目的,但现在我将向您展示如何使用 Android SDK 工具来做本质上相同的事情。

然而,还有一件事需要先做。如果你碰巧使用 Linux 或者 Mac OS X,你很可能已经安装了 Python 的一个版本。在这两个平台上,您可以通过打开终端窗口并启动 Python 来确定。图 2-22 显示了在我运行 OS X 雪豹 10.6.5 版本的 Mac Mini 上的样子。

images 注意任何 2.6 版本的 Python 都应该可以在主机上工作,但是要注意 SL4A 是基于 2.6.2 的,以防你在从主机远程运行脚本到设备时看到一些奇怪的行为。

images

***图 2-22。*运行在 Mac OS X 上的 Python

默认情况下,Python 不会安装在 Windows 上。要安装 Python,请转到[python.org](http://python.org)并找到发布页面([python.org/download/releases](http://python.org/download/releases))。在那里,您可以找到所有的主要版本,包括版本 2.6.6。选项包括 32 位和 64 位 Windows 版本。出于测试目的,我选择了 64 位版本。双击。msi 文件启动安装程序,提示您是否允许安装。

完成后,您可以修改 Windows 路径来添加 Python26 目录。完成该任务的最快方法是按下 Windows 键并键入单词 system 。您应该在控制面板标题下看到编辑系统环境变量选项。单击该行启动系统属性对话框,然后单击环境变量按钮。在系统变量部分找到 Path 变量,然后单击编辑按钮。这将显示一个带有当前系统路径语句的文本框。导航到字符串的末尾,并添加以下文本:

;C:\Python26

这将把 Python 目录添加到搜索路径中,并使 Python 可以在任何命令窗口中使用。要验证您是否正确安装了 Python 并正确设置了路径,请启动一个命令窗口并键入单词 Python 。你应该会看到类似图 2-23 的东西。

images

***图 2-23。*运行在 Windows 7 上的 Python

远程连接到设备

从主机连接到您的设备需要 Android SDK 和 ADB 工具。我将在后面的章节中深入研究这个工具,但是现在我将讨论如何使用这个命令来远程连接到设备。实际上,您正在设置一个代理,通过特定端口将通信传递给设备。

要从 Windows 连接到您的设备,需要克服一些障碍。第一个也可能是最大的障碍是让 Windows 识别你的设备。下载 SDK 包时的可选组件之一是用于 Windows 的 USB 驱动程序。如果您希望计算机识别您的设备,这是必需的。图 2-24 显示了可用包窗口下的该项目。

选择此选项,然后单击“安装选定内容”按钮。这会将驱动程序文件下载到 SDK 根目录下的子目录中。接下来的步骤将取决于您尝试连接到 Windows 电脑的设备类型。

images

***图 2-24。*用于 Windows 的 USB 驱动程序

如果你的设备碰巧是 G1、myTouch 3G、Verizon Droid 或 Nexus One,你应该已经准备好了。如果没有,你将有更多的工作要做。连接到另一个设备,如 HTC EVO 4G 智能手机,需要将附加信息添加到驱动程序.inf文件中,以便驱动程序识别该设备。这个文件可以在 google-usb_driver 子目录中的 SDK 所在的目录树中找到。您真正需要知道的唯一事情是设备的供应商 ID (VID)和产品 ID (PID)。你可以在谷歌上搜索几下,为你的手机找到合适的号码。另一个寻找 ADB 连接问题答案的好地方是 Google groups 上的 Android Developers group([groups.google.com/group/android-developers](http://groups.google.com/group/android-developers))。

作为最后的手段,你必须进入侦查模式。当您将设备连接到 Windows 时,它会尝试为您安装合适的驱动程序。如果它找不到,你会得到一个弹出信息,表明该设备未能正确安装。此时,您必须使用设备管理器来发现所需的信息。启动设备管理器最简单的方法是按下 Windows 键,开始在搜索框中键入设备。这会给你一个选项列表,包括设备管理器。选择设备管理器会弹出一个类似于图 2-25 的对话框。

由于 Windows 无法识别您连接的设备,您将看到 ADB 列在“其他设备”下。现在,您必须右键单击 ADB 设备并选择 Properties 来查找您的设备的 VID 和 PID 列表。我将向您展示 HTC EVO 4G 的外观,希望您可以使用相同的方法来连接您的设备。

images

图 2-25 。Windows 设备管理器显示未知设备

显示 ADB 属性屏幕后,您需要选择详细信息选项卡,然后从下拉框中选择硬件 id。图 2-26 显示了您应该看到的 HTC EVO 4G 信息。接下来,您需要 VID 和 PID 信息来修改驱动程序.inf文件。

images 注意如果你采用了 Android SDK 的默认值。exe 安装程序,你会在以下目录找到 USB 驱动文件:C:\ Program Files(×86)\ Android \ Android-SDK-windows \ Google-USB _ driver。

images

***图 2-26。*亚行设备的设备属性

有了 VID 和 PID,您需要编辑 android_winusb.inf 文件。您需要添加的兴趣行如下:

; ;HTC EVO 4G %SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C8D![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) %CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C8D&MI_01

进行更改的最简单方法是使用记事本编辑文件。如果您希望能够将记事本保存回同一个目录,您必须以管理员身份启动记事本。为此,你可以按下 Windows 键,开始输入记事本。右键单击弹出窗口中的记事本,然后选择以管理员身份运行。打开记事本后,导航到google-usb_driver目录,然后双击android_winusb.inf文件。当您打开文件时,您需要找到带有标签[Google]的部分。NTx86]并复制下面的前三行。对我来说,这是 HTC 梦想设备的一个入口。将这些行粘贴到[Google.NTx86][Google.NTamd64]部分的末尾,更改您之前发现的 VID 和 PID 值。我没有改变 VID,因为 EVO 4G 是 HTC 设备。

现在,您应该准备好使用 USB 电缆将设备连接到主机。我还必须将设备上的 USB 连接类型改为 HTC Sync。这将在手机上打开一个对话框,因为它试图连接到主机上的 HTC Sync 应用。此时,您可以按下设备上的后退键,或者等待连接尝试超时。通过再次启动设备管理器并右键单击其它设备下的 ADB 设备,更新 ADB 设备的驱动程序。从弹出对话框中选择更新驱动程序,然后从下一个屏幕中选择浏览我的电脑以查找驱动程序软件。该对话框允许您浏览放置编辑过的.inf文件的目录。确保您选择了google-usb_driver目录,然后单击下一步。如果一切顺利,您应该会看到一个屏幕,指示 Windows 已成功更新您的驱动程序软件。

最后,在您的设备上启用 USB 调试。这发生在设备设置屏幕上。从设置,触摸应用,然后开发。在开发屏幕上,您必须选中 USB 调试旁边的复选框来启用该功能。

如果您成功地正确配置了一切,您将能够打开一个命令窗口或终端会话并发出adb命令。在 Windows 上,你应该可以看到你的设备带有如图 2-27 所示的命令。

images

***图 2-27。*输出adb设备命令

如果您使用的是 Linux 或 Mac OS X,您应该不会有任何连接设备的问题。在连接 USB 电缆后,我可以在两个平台上使用 adb 设备命令查看 HTC EVO 4G,无需任何额外的步骤。

设备设置

远程连接到您的设备需要在设备和桌面上执行一些步骤。在设备上,您必须从解释器屏幕启动服务器。为此,您必须完成以下步骤:

  1. 从设备上的所有应用屏幕启动 SL4A。
  2. 按菜单按钮,然后选择查看选项。
  3. 从列表中选择口译员。
  4. 再次按下菜单按钮,然后选择启动服务器。
  5. 如果您想通过 WiFi 连接,请选择公共;如果您通过 USB 连接,请选择私人。

此时,您的服务器应该已经启动,并准备好通过特定端口进行访问。要找出分配了什么端口号,您必须通过从设备屏幕顶部向下拖动状态窗口来打开脚本监视器。这将显示 SL4A 服务和此时应该为 1 的运行脚本的数量。要确定端口号,您必须单击(触摸)SL4A 服务线路以调出脚本监视器。您应该会看到类似于图 2-28 的内容,端口号显示在 localhost: string 之后。

images

图 2-28。 SL4A 服务器模式,端口地址

您现在可以远程连接到您的设备了。要实现这一点,您必须再输入几个命令来定义一个环境变量并启用端口转发。这些命令和 Python 本身需要从以管理员身份运行选项启动的命令窗口中运行。环境变量必须命名为 AP_PORT,默认值为 9999,这样示例才能运行。为了启用端口转发,使用 adb 命令将端口 9999 的所有内部 tcp 流量转发到远程服务器的端口号。图 2-29 显示了这在 Windows 上应该是什么样子。

Linux、Mac OS X 和 Windows 之间的命令略有不同。在 Linux 和 Mac OS X 上,您可以使用 export 命令创建一个环境变量:

$ export AP_PORT=9999

您可能还想将它添加到启动脚本中。在 Linux 上,这将是~/.bash_profile~/.bashrc。在 Mac OS X 上,它会出现在你的主目录中的.bash_profile

images

***图 2-29。*远程控制的 Windows 环境变量和 adb 命令

在基于 Windows 的计算机上,您可以通过之前使用的环境变量屏幕将其添加为永久环境变量。这次你用 new 按钮创建一个新的用户变量,然后输入名称 AP_PORT 和值 9999 ,如图 2-30 中所示。

images

***图 2-30。*为 AP_PORT 创建一个永久的 Windows 环境变量

执行简单的程序

一旦你把所有东西都连接起来,你可能想开始使用 Python 和 IDLE 进行探索。IDLE 是一个用 Python 和 Tkinter GUI 工具包编写的简单的跨平台集成开发环境(IDE)。首先,它提供了一个 Python 解释器命令行,您可以在其中键入代码行,并获得代码结果的即时反馈。还可以打开编辑器窗口来创建和修改 Python 脚本。可以保存这些脚本,然后在主解释器窗口中运行输出。在执行脚本之前,您还可以在编辑器窗口中获得关于任何语法错误的反馈。

这种方法的巧妙之处在于,您可以在桌面计算机上编写代码,并在您的设备上测试代码。请记住,您获取的任何库都需要在您的桌面计算机上可用。你需要注意的另一件事是 Python 版本号。截至本书写作时,SL4A 使用的是 Python 的 2.6.2 版本。在 2.6.2 之后,您可能可以使用任何 2.6 版本,但是请注意,如果您在桌面上使用不同于 2.6.2 的版本,您可能会遇到一些难以理解的兼容性问题。图 2-31 显示空闲运行“Hello World”程序。

您需要将android.py文件复制到与代码相同的目录中,或者复制到开发机器上的默认 Python 安装目录中。当您在脚本中执行import android代码时,解释器必须能够定位android.py

images

图 2-31。 Python 空闲程序连接到设备

您还可以使用 IDLE 在单独的编辑器窗口中编辑和运行脚本。如果您单击文件菜单并选择新窗口,您将看到一个空白的编辑器屏幕,用于输入 Python 代码。您也可以在即时窗口和编辑器窗口之间进行剪切和粘贴。用“Hello Android World”代码尝试一下,只需要少量的编辑就可以删除额外的提示字符。图 2-32 显示了结果。

images

图 2-32。 Python 空闲编辑窗口

开始探索 SL4A 的另一个很好的方法是看看随 Python 解释器一起安装的一些示例程序。其中之一是Test.py,它是一个示例程序,使用 SL4A 支持的许多对话框类型以及一些其他测试用例。这是一个导入默认安装在设备上的几个模块的程序,但是除非您明确安装它们,否则它们可能不会在您的主机上。

您可以通过启动 SL4A 并从文件列表中选择Test.py在您的设备上运行Test.py。当你从 SL4A 主窗口中选择一个文件时,你会看到一个弹出的小对话框,里面有很多图标。图 2-33 显示了您应该看到的内容。

images

图 2-33。 SL4A 脚本启动选项

终端图标在设备的终端窗口中启动脚本,因此当齿轮图标在后台启动脚本时,您可以看到任何错误或调试消息。选择铅笔图标将在文本编辑器中打开脚本,而磁盘图标将允许您重命名脚本。如果您选择垃圾桶图标,您将有一次机会改变删除的想法,因为将会打开一个“是/否”对话框,提示您确认这是您想要做的。

脚本编辑器提供了一种在设备上输入或编辑脚本的简单方法。它在小型设备上效率不是很高,但在平板电脑等大屏幕设备上效果相对较好。在将脚本加载到实际设备之前,您也可以在模拟器中使用它来测试脚本。完成编辑后,您必须按下菜单按钮,调出选项菜单,以保存并退出或保存并运行。图 2-34 显示了它在你的设备上的样子。

images

图 2-34。 SL4A 脚本编辑器选项菜单

Preferences 按钮将调出与之前相同的菜单,其中包含 SL4A 应用的所有选项。选择帮助按钮将显示一个对话框,其中有三个选项,包括 Wiki 文档、YouTube 截屏和终端帮助。前两个选项将打开一个 web 浏览器,并重定向到您在 SL4A 项目的主页上找到的相同页面。

API 浏览器按钮将显示一个列出所有可用 API 函数的屏幕(参见图 2-35 )。

images

图 2-35。 API 浏览器工具

如果你长按(意味着触摸一条线并保持),你会看到一个类似于图 2-36 中的屏幕。选择“提示”将会弹出一个额外的对话框,其中有单独的文本框,供您填写 API 函数调用所需的必要参数(参见图 2-37 )。这是探索一些更复杂的 API 函数并让 SL4A 解释器引导您填写正确条目的好方法。

images

图 2-36。 API 浏览器选项

images

图 2-37。 API 浏览器提示选项

总结

这一章包含了很多信息,但是希望到此时,您已经在设备和主机上完全配置好了 SL4A 测试环境。主机的操作系统不重要,所以你可以自由选择你最喜欢的平台。如果你碰巧在某个地方遇到困难,谷歌搜索是你的朋友。

作为总结,让我们确定本章的要点:

  • 安装 SL4A :这发生在物理设备上或者模拟器中。您首先安装 SL4A。apk 文件,然后从 SL4A 菜单添加解释器。
  • 安装 Android SDK :这发生在你的开发机器上,它可以运行 Linux、Mac OS X 或 Windows。如果您的操作系统还没有安装 Java,您可能还必须先安装它。
  • 在 Windows 上配置 USB 驱动:这可能是最棘手的一步,不幸的是,如果你是在 Windows 机器上开发,这是无法回避的。您必须让这个工作在您的开发机器和一个物理设备之间建立通信。
  • 在 Windows 上安装 Python:同样,你不会在任何库存的 Windows 机器上找到 Python,所以你必须自己安装。幸运的是,这很简单。

不要害怕在这里踢轮胎。如果你不习惯在你闪亮的新手机上尝试这些东西,可以使用模拟器。这就是它存在的目的。要知道,总有一天你将不得不使用该器件,因为并非真实器件的所有功能都可以在仿真器中模拟。好消息是,这些能力的列表非常短。

三、Android SDK 导航

本章将深入探讨 Android 软件开发工具包(SDK)以及如何使用它来开发针对 Android 脚本层的代码。

images 本章所有例子都基于 Android SDK 的 release 8。

在这一章中,我会这样分解它:

  • 费力地阅读 SDK 文档
  • 检查不同的 SDK 组件
  • 使用 Android 模拟器进行测试
  • 探索 Android 调试桥(ADB)
  • 使用 Dalvik 调试监控服务(DDMS)进行调试和更多工作

准备好进入 Android SDK 的世界吧。Android 的每个版本都有一个特定于该版本的 SDK。在构建原生 Android 应用时,您可以选择特定的版本,以便利用最新的功能。这意味着您可以使用最新的 SDK 版本,并且仍然可以构建和测试在旧版本上运行的应用。我将使用版本 8 (r8)作为目标,因为这是我将测试的所有设备上的 Android 版本。

费力地阅读 SDK 文档

如果您查看 SDK 安装的根目录,您会看到一个名为docs的条目。这实际上是主要 Android 开发者网站上文档的副本,供本地访问。打开docs目录,你会看到几个.html文件。Index.html是您打开网络浏览器并导航至developer.android.com时看到的画面。Offline.html提供了帮助您安装 SDK 的链接(如果您还没有安装的话),以及一个指向主站点的注释,“获取最新文档和全功能体验”

第一次浏览 Android 开发者网站时,你不难被淹没。那里的信息量非常惊人。最好的办法是退后一步,挑选几个你想阅读的领域,然后专注于这些主题。每个页面的顶部都有一个搜索框,它将按排序顺序返回您的请求,还有一个选项卡列表,让您可以快速将结果缩小到文档树的特定区域,包括开发指南、参考、开发人员小组、Android 源代码和博客。

如果你没有从架构的角度来看 Android 平台,那么你可能想从应用基础([developer.android.com/guide/topics/fundamentals.html](http://developer.android.com/guide/topics/fundamentals.html))部分开始,以便很好地了解 Android 的优势。我们详细讨论了许多顶级概念,这将有助于您理解应用如何与特定 Android 应用编程接口(API)提供的底层功能进行通信。在本书的上下文中,特别感兴趣的是不同的进程如何使用远程过程调用(RPC)进行通信。Android 脚本层(SL4A)使用这种机制将信息从脚本解释器传递到 Android API。

了解 Android 中的内容提供者将使您更容易使用脚本可用的信息。图 3-1 显示了 Android 开发者文档的开发指南部分的快照,内容统一资源标识符(URI)是什么样子的以及如何解释它。内容 URI 的关键部分是权威部分,它看起来很像一个网址。一旦你看到这个模式,你就能读懂其中的一个 URIs,并确切地知道它的意思。

images

***图 3-1。*安卓内容供应器描述

另一个值得花时间阅读的地方是用户界面指南部分。任何以某种方式与用户交互的程序都应该努力遵守谷歌在按钮、图标和文本输入方面的惯例。SL4A 提供了对大量用户界面元素的访问,您最好花些时间了解如何以及何时使用它们。一个很好的例子是什么时候使用上下文菜单,什么时候使用选项菜单。一个选项菜单通常在用户按下菜单按钮时出现,而一个上下文菜单类似于你在桌面操作系统上右击鼠标时得到的菜单。在 Android 上,长按或触摸并保持动作等同于用鼠标右键单击。

在第二章的中,我介绍了一个名为makeToast的 Android 实用函数。这个小函数创建了一个短的弹出消息,出现在屏幕上,但没有获得焦点或暂停当前活动。它是通知标题下的一类消息的一部分。一个吐司通知是给用户反馈他们刚刚做的事情的一个简单方法,比如设置一个闹钟在特定的时间响起。Android 还支持状态栏通知,以便在系统的状态栏中添加图标和可选的短消息,以及通知窗口中的扩展消息。你也可以发出声音或振动来给予额外的反馈。

对话框是其他值得一读的用户界面元素。DatePickerDialog 和 TimePickerDialog 是两个特殊的对话框,使在小屏幕上输入日期和时间变得更加容易。ProgressDialog 是一个用户反馈元素,用于为长期运行的活动提供进度信息。毫无疑问,AlertDialog 是所有对话框中最灵活的,也可能是最常用的。警告对话框还可以包含项目列表、复选框和单选按钮。图 3-2 显示了一个带有文本和按钮的警告对话框。

images

***图 3-2。*带有文本和按钮的警告对话框

文档中另一个有趣的地方是 WebView。如果你想建立一个除了按钮和对话框之外的用户界面,你必须使用 WebView。在第九章中,我会花很多时间讨论如何使用 WebView 构建界面,所以理解这些基础知识会有所帮助。

检查不同的 SDK 组件

如果您查看安装 Android SDK 的目录树,您应该会看到包含文档、示例代码和许多工具的文件夹列表。还有一个名为market_licensing的目录,里面有关于如何销售你完成的应用的信息。在 SDK 的第 8 版中,Google 对目录结构做了一些更改,影响了一些更常用工具的位置。如果您为以前安装的 SDK 版本设置了快捷方式或修改了路径,您将需要更改目标目录。

图 3-3 显示了 Windows 7 机器上顶层目录的屏幕截图。这显示了安装在 64 位版本的 Windows 7 上的 32 位 SDK,因此有了Program Files (x86)父目录。

images

***图 3-3。*Windows 上的 Android SDK 文件结构

如果您想要在 Windows 上创建 SDK 管理器应用的快捷方式,请确保在android-sdk-windows目录中右键单击 SDK 管理器应用,将其拖到桌面上,然后选择在此处创建快捷方式。如果你不这样做,你将会移动应用或者复制一个不能在桌面上运行的应用。

导航到 tools 子目录将显示许多可执行文件。第一个也可以说是最重要的是 Android 调试桥(ADB)工具。您可以使用 ADB 将文件从本地机器移动到设备,就像从命令行复制文件一样。我将在本章的稍后部分深入研究 ADB。SDK Manager 应用是您进行 SDK 更新、创建和启动虚拟设备以及查找第三方插件的起点。

使用 Android 模拟器进行测试

在使用 Android 模拟器之前,您必须做的第一件事是配置一个目标设备。Android 虚拟设备(AVD)由许多不同的文件组成,包括配置和虚拟存储,仿真器需要这些文件来完成工作。您可以根据需要创建任意数量的 avd 来模拟不同的设备。创建 AVD 最简单的方法是使用 SDK 和 AVD 管理器应用,如图 3-4 所示。

images

图 3-4。 Android SDK 和 AVD 管理器工具

要开始创建新的 AVD,只需单击 new 按钮。这将弹出一个对话框,如图 3-5 所示。你要做的第一件事是给你的新 AVD 一个名字。您应该使其具有描述性,但是名称不能有任何空格。选择名称后,您必须选择一个目标环境。最后,你需要为你的 SD 卡选择一个尺寸。如果您专门使用它来测试 SL4A,应该不需要太多空间。本例中使用了 100 MB 的值。

images 提示三星 Galaxy Tab 等其他目标设备可通过 SDK Manager 获得,而其他设备可能直接从供应商处获得,如 Barnes & Noble Nook Color。您还可以修改其中一个通用设备,以适应真实设备的功能,如硬件键盘或特定的屏幕尺寸。

屏幕分辨率将默认为内置设备之一,除非您选中分辨率单选按钮并指定具体尺寸。要添加或删除硬件功能,请单击硬件部分旁边的新建按钮,并从下拉列表中选择一项功能。要删除键盘等功能,首先通过单击“新建”按钮添加键盘支持,然后通过单击“值”列并选择“否”来更改其值

images

***图 3-5。*新的 AVD 对话框

您可以从 SDK 管理器屏幕启动任何已定义的 avd,方法是选择您的设备,然后单击启动按钮。这将打开另一个对话框,让您有机会在它开始之前设置一些选项。您想要更改的选项之一是“将显示比例调整为实际大小”复选框。这将启用“屏幕大小”和“监视器 DPI”文本框,您可以在其中选择希望模拟器在屏幕上显示的大小。这将取决于您的实际显示器尺寸,尽管我发现对于典型的 20 英寸显示器来说,10 英寸是个不错的选择。

最后一个复选框“擦除用户数据”为您提供了一种快速启动虚拟设备的方法,无需任何先前会话中的数据。此功能允许您测试首次运行时行为不同于正常行为的应用,而无需每次都重新创建新的 AVD。图 3-6 显示了屏幕尺寸选项设置为十英寸时该对话框的样子。

images

图 3-6。 AVD 启动选项对话框

对于带键盘的设备,从主机键盘到设备上的操作有一组标准的映射。表 3-1 显示了保存在default.keyset文件主目录下的.android子目录中的默认定义集。您可以通过编辑default.keyset文件或创建您自己的keyset文件,然后在模拟器命令行上添加–keyset选项来更改这些设置。

***表 3-1。*仿真器键映射

| `BUTTON_CALL` | 第三子代 | | :-- | :-- | | `BUTTON_HANGUP` | 法乐四联症 | | `BUTTON_HOME` | 主页 | | `BUTTON_BACK` | 逃跑 | | `BUTTON_MENU` | F2, 页面上传 | | `BUTTON_STAR` | Shift+F2,向下翻页 | | `BUTTON_POWER` | F7 | | `BUTTON_SEARCH` | F5 | | `BUTTON_CAMERA` | Ctrl+小键盘 _5,Ctrl+F3 | | `BUTTON_VOLUME_UP` | 小键盘+Ctrl+F5 | | `BUTTON_VOLUME_DOWN` | 键盘减号,Ctrl+F6 | | `TOGGLE_NETWORK` | F8 | | `TOGGLE_TRACING` | F9 | | `TOGGLE_FULLSCREEN` | Alt-Enter | | `BUTTON_DPAD_CENTER` | 键盘 _5 | | `BUTTON_DPAD_UP` | 键盘 _8 | | `BUTTON_DPAD_LEFT` | 键盘 _4 | | `BUTTON_DPAD_RIGHT` | 键盘 _6 | | `BUTTON_DPAD_DOWN` | 键盘 _2 | | `TOGGLE_TRACKBALL` | F6 | | `SHOW_TRACKBALL` | 删除 | | `CHANGE_LAYOUT_PREV` | 键盘 _7,Ctrl+F11 | | `CHANGE_LAYOUT_NEXT` | 键盘 _9,Ctrl+F12 | | `ONION_ALPHA_UP` | 键盘 _ 乘法 | | `ONION_ALPHA_DOWN` | 键盘 _ 除法 |

用键盘启动一个普通的 AVD 将会弹出一个类似于图 3-7 的屏幕。

images

***图 3-7。*通用仿真窗口

除了一些小的例外,模拟器应该像真实设备一样工作。您应该记住电脑键盘上的哪些键是模拟硬件按钮键的,因为您会经常使用它们。默认情况下,PC 到设备的映射是 Home 到 Home,F2 到 Menu,Esc 到 Back,F5 到 Search。电脑上的鼠标取代了你在真实设备上的手指。左击鼠标与触摸或按压屏幕是一样的。如果您点击屏幕底部中间类似棋盘的图标,您将启动如图图 3-8 所示的应用屏幕。

images

***图 3-8。*模拟器应用启动屏幕

要查看通知区域中的消息,您首先必须在设备屏幕顶部附近,在与信号强度、电池电量和时间图标相同的白色条区域中单击并按住鼠标。接下来,用鼠标像用手指一样向下拖动。这类似于拉下窗帘。该动作将随时显示通知窗口,如果没有通知,则显示“无通知”消息。同样的技术可以用来模拟从左到右的滑动,反之亦然,使用鼠标点击、按住并拖动仿真器设备屏幕。

蓝牙在模拟器中不起作用,所以如果你需要使用蓝牙进行测试,你必须在真实的设备上进行。WiFi 也是没有的。模拟器支持 3G 数据连接,这意味着你可以连接到互联网。您可以通过选择联系人或直接输入电话号码来模拟呼叫。所有呼叫都显示在呼叫日志中,为您提供了另一个测试数据源。虽然您可以模拟拨打和接听电话,但没有真正的语音功能,所以不要期望实际拨打真正的电话。这也意味着你不能在模拟器上测试任何语音识别功能。

出于测试目的,您可能需要在模拟器上配置电子邮件帐户,这就像在真实设备上一样。图 3-9 显示了首次启动应用时的打开屏幕。

images

***图 3-9。*模拟器电子邮件配置屏幕

要配置 Gmail 帐户,请输入您的完整地址,包括[@gmail.com](http://@gmail.com)部分。输入密码后,单击“下一步”按钮。如果您输入的信息正确,您应该会看到一个屏幕,要求您输入一个(可选的)帐户名称和一个要添加到外发邮件中的名称(签名)。

为了方便起见,您可以在模拟器主屏幕上添加一个指向 SL4A 应用和脚本文件夹的快捷方式。为此,单击并按住主屏幕上的任意位置。这将启动添加到主屏幕对话框。此时,您可以选择添加快捷方式、小工具、文件夹或壁纸。选择快捷方式条目,为 SL4A 应用添加一个快捷方式。这将显示另一个对话框,其中包含所有支持快捷方式的可用应用和操作。选择应用,并从选择活动对话框中选择 SL4A。

另一种快速访问脚本的简便方法是在主屏幕上添加一个文件夹。除了从“添加到主目录”对话框中选择“文件夹”之外,您的操作方式与添加快捷方式相同。选择文件夹后,您应该会看到一个标有“选择文件夹”的新对话框,其中包含可用文件夹的列表。选择标记为 Scripts 的条目,您应该已经准备好了。现在,当您点击主屏幕上的脚本图标时,您应该会看到类似图 3-10 的屏幕。这将显示该文件夹中所有脚本的列表,并让您一键访问运行它们。我将在第八章的中使用这个特性来构建一个方便的手机设置脚本,只需点击两下就可以改变多个设置。

images

***图 3-10。*主屏幕脚本文件夹

另一个不能在模拟器上运行的是实时短信。你可以打开消息应用,输入一条消息,但实际上什么也不会发出。如果您需要进行测试,它将使用传出消息填充数据库。要模拟一条短信,你必须使用 ADB 工具,这是下一个讨论的话题。

安卓调试桥

第二章对 Android Debug Bridge (ADB)有一个简单的介绍,但实际上只是刷了一下你可以用这个工具做的事情的表面。亚行实际上需要三个独立的组成部分来完成它的工作。在您的开发机器上,ADB 由一个客户端和一个服务器组成。ADB 服务器处理运行在开发机器上的客户机和运行在仿真器或目标设备上的守护进程之间的所有通信。

命令行选项用于指示 ADB 执行特定任务。表 3-2 给出了这些命令的简要描述。对于安装、同步和卸载命令,有一些选项可用于修改命令的行为方式。如果您碰巧运行了一个仿真器并且连接了一个真实的设备,那么您必须指定您希望 ADB 命令在哪里执行。要将 ADB 命令导向真实设备,使用选项–d,对于仿真器,使用–e

***表 3-2。*亚行命令列表

| `adb push ` | 将文件/目录复制到设备 | | :-- | :-- | | `adb pull []` | 从设备复制文件/目录 | | `adb sync [ ]` | 仅在发生更改时复制主机->设备(-l 表示列出但不复制) | | `adb shell` | 交互式运行远程 shell | | `adb push ` | 将文件/目录复制到设备 | | `adb shell ` | 运行远程 shell 命令 | | `adb emu ` | 运行仿真器控制台命令 | | `adb logcat [ ]` | 查看设备日志 | | `adb forward ` | 前向插座连接 前向规格如下: `tcp: localabstract: localreserved: localfilesystem: dev: jdwp:`(仅远程) | | `adb jdwp` | 列出主持 JDWP 传输的进程的 PID | | `adb install [-l] [-r] [-s] ` | 将此包文件推送到设备上并安装 ( `-l`表示正向锁定 app) ( `-r`表示重新安装 app,保留其数据) ( `-s`表示安装在 SD 卡上,而不是内部存储) | | `adb uninstall [-k] ` | 从设备 中移除此应用包(`-k`表示保留数据和缓存目录) | | `adb bugreport` | 返回设备中应包含在错误报告中的所有信息 | | `adb help` | 显示此帮助消息 | | `adb version` | 显示版本号 |
文件和应用

有三个命令用于处理主机与仿真器或物理设备之间的文件复制。push将文件从主机拷贝到目标,而pull将文件从目标拷贝到主机。sync尝试在主机和目标上的目录之间同步文件。您也可以将-l选项传递给sync,它将简单地列出目录的内容。

.apk文件安装或卸载到仿真器或物理设备分别使用 adb installuninstall命令。install命令的选项包括-l向前锁定应用、-r重新安装保留所有旧数据,以及-s将应用安装在 SD 卡上而不是内部设备存储上。

images 提示您可以使用 adb push工具在仿真设备上快速加载触点以进行测试。如果您在 Gmail 帐户中存储了联系人,您可以轻松地将其导出到 vCard 文件,然后使用 adb push将其移动到设备上的 SD 卡中。剩下的就是启动通讯录,从 SD 卡导入 vCard 文件。

贝壳

adb shell命令提供了一种向设备发送 shell 命令并显示结果或在本地启动交互式 shell 的方法。您可以使用shell命令来自动测试一个设备。这使用了shell input keyeventsendevent命令。Input 是一个应用,它位于设备的/system/bin目录中,可以模拟任何类型的键盘输入。您可以使用以下命令推送文本字符串:

adb shell input text "ANDROID"

要模拟在物理或虚拟键盘上按键,请在输入命令中使用keyevent限定符,并使用一个整数来表示您希望调用的特定键码。键码列表如表 3-3 所示。您可以通过以下方式模拟按下菜单键:

adb shell input keyevent 1

images

images

如果您计划开发任何与电话功能交互的应用(发起电话呼叫;发送或接收 SMS 消息),您必须使用telnet命令连接到仿真器控制台。在 Windows 7 上,默认情况下没有安装 Telnet 程序。幸运的是,它是操作系统的一部分,只需启用即可。要启用 Telnet,您必须打开控制面板并选择程序类别。这将弹出一个类似于图 3-11 的窗口。您也可以通过按 Windows 键并键入单词功能来直接启动 Windows 功能对话框。这将显示一个选项列表,包括标题“控制面板”下的几个选项。选择“打开或关闭 Windows 功能”项将弹出如图 3-12 所示的对话框。

images

图 3-11。 Windows 7 控制面板程序页面

要启用 Telnet 客户端程序,只需在 Windows 功能对话框中单击 Telnet 客户端旁边的复选框。如图图 3-12 所示。如果您希望您的计算机也作为 Telnet 服务器,您可以选中 Telnet 服务器框,尽管出于安全原因,大多数 Windows 防火墙会阻止传入的 Telnet 流量。

images

图 3-12。 Windows 7 功能对话框

假设您已经使用正常的默认值启动了一个 ADB 设备,您可以在 Linux 或 Mac OS X 上使用以下命令从终端窗口或 Windows 的命令提示符启动一个 telnet 会话:

telnet localhost 5554

现在,您可以直接向设备发出命令了。图 3-13 显示了 telnet 会话开始时help命令的输出。

images

***图 3-13。*在 Linux 上 Telnet 会话到仿真器

模拟传入的 SMS 消息需要如下命令:

sms send 3015551212 "This is a test SMS message from Telnet"

在模拟器上,您应该会看到一条消息。打开消息应用,您应该会看到类似图 3-14 的内容。

images

***图 3-14。*显示模拟短信的短信应用

为了测试基于位置的应用,您可以使用geo命令,它将发送一个 GPS NMEA 语句或一个简单的 GPS 定位。这两个命令如下所示:

`geo fix -82.411629 28.054553

geo nmea $GPGGA,001431.092,0118.2653,N,10351.1359,E,0,00,,-19.6,M,4.1,M,,0000*5B`

如果除了模拟当前的纬度和经度之外,您还想做任何事情,您将不得不使用NMEA命令。$GPGGA代码代表全球定位系统定位数据。字段从左到右依次是句子标识符($ GPGGA)、时间(00:14:31.092)、纬度、北纬、经度(103 度 51 分 13.59 秒)、卫星数量、水平精度因子(HDOP)、海拔高度、WGS84 椭球面上方的大地水准面高度、自上次 DGPS 更新以来的时间、DGPS 参考站 id 和校验和。

有一个 shell 命令允许您在模拟器上更改日期和时间。如果您正在测试任何基于时间的逻辑,如警报或运行时间应用,这将非常方便:

adb shell date secs

使用这个命令的唯一缺点是,您必须输入自 1970 年 1 月 1 日以来的秒数;也称为 UNIX 纪元。在 Linux 或 Mac OS X 终端提示符下,可以使用以下命令来确定当前时间的秒值:

date +%s

作为 Python 标准库的一部分,Python 语言有许多方便的特性和功能。Python 只用几行代码就能处理时间和日期操作。下面是一个将任何日期和时间转换成正确的秒值的简短脚本:

清单 3-1。 Python 纪元时间转换器

`#!/usr/bin/python #-----------------------------

This script will convert arg1 (Date) and arg2 (Time) to Epoch Seconds

import sys, time, datetime

if len(sys.argv) < 3: print "Usage pyEpoch YYYY-MM-DD HH:MM" else: intime = sys.argv[1] + " " + sys.argv[2]

t = time.strptime(intime, "%Y-%m-%d %H:%M") t = datetime.datetime(*t[:6]) print "Epoch Seconds:", time.mktime(t.timetuple())`

在谷歌上快速搜索 UNIX 纪元转换器,会出现几个可以转换日期和秒的网站。请注意,执行该命令将会生成一条错误消息,指出settimeofday失败了,但它确实工作了。图 3-15 显示了一个设置了若干时间的 Windows 命令提示符,并使用不带参数的相同日期命令读取时间,以显示当前时间。

images

***图 3-15。*用 shell 命令设置仿真器日期

您可以使用 ActivityManager 的命令行界面启动模拟器上的任何应用,如下所示:

adb shell am start command

图 3-16 显示了当您在 telnet 提示符下输入 am 时所给出的帮助输出。

images

***图 3-16。*使用 shell am 命令在模拟器上启动活动

要启动 web 浏览器,您可以使用以下命令:

adb shell am start 'http://www.google.com'

命令adb shell dumpsys提供了关于任何连接的 Android 设备的当前状态的几乎所有信息。如果你在模拟器上运行这个命令,你会得到一个可用子命令的列表,如表 3-4 所示。

***表 3-4。*dump sys 子命令列表

| `SurfaceFlinger` | `accessibility` | `account` | | :-- | :-- | :-- | | `activity` | `alarm` | `appwidget` | | `audio` | `backup` | `battery` | | `batteryinfo` | `clipboard` | `connectivity` | | `content` | `cpuinfo` | `device_policy` | | `devicestoragemonitor` | `diskstats` | `dropbox` | | `entropy` | `hardware` | `input_method` | | `iphonesubinfo` | `isms` | `location` | | `media.audio_flinger` | `media.audio_policy` | `media.camera` | | `media.player` | `meminfo` | `mount` | | `netstat` | `network_management` | `notification` | | `package` | `permission` | `phone` | | `power` | `search` | `sensor` | | `simphonebook` | `statusbar` | `telephony.registry` | | `throttle` | `uimode` | `usagestats` | | `vibrator` | `wallpaper` | `wifi` | | `window` | | |

对于模拟器,命令adb shell dumpsys battery的输出如下所示:

Current Battery Service state: AC powered: true USB powered: false status: 2 health: 2 present: true level: 50 scale: 100 voltage:0 temperature: 0 technology: Li-ion

另一个有趣的子命令是location。如果您查看模拟器的命令adb shell dumpsys location的输出,您不会看到太多。在真实设备上运行同样的命令,你会看到各种信息。如果您检查命令adb shell dumpsys activity的输出,您会看到一长串关于设备当前活动状态的信息。

您可以通过command adb shell dumpsys package获得由PackageManagerService维护的信息列表。这个命令的输出有许多不同的部分,第一部分是活动解析器表。本节包含 MIME 类型、非数据动作和 MIME 类型动作的列表,以及用于启动特定动作的意图。非数据操作启动不需要任何数据即可启动的活动。其中一个例子就是com.android.contacts.action.LIST_ALL_CONTACTS。从名称上看,这种意图的行为相当明显,但是您可以看到使用该命令的结果:

adb shell am start –a com.android.contacts.action.LIST_ALL_CONTACTS

要通过意图机制启动更复杂的操作,必须指定许多不同的字段。如图 3-16 中的所示,你有许多选项可用,包括-a指定动作、-c指定类别、-d指定数据 URI、-t指定 MIME 类型、-e指定附加项目。您可以使用以下命令启动联系人应用来添加条目:

adb shell am start -a android.intent.action.INSERT -d 'content://contacts/people' -t![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 'vnd.android.cursor.dir/person' -c 'android.intent.category.DEFAULT' -e 'name' 'Paul'![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) -e 'phone' '1112223333'

在这一点上,你可能想参考 Android 开发者文档,并仔细阅读 Intent 主题。理解如何使用意图来启动一个活动将会让你洞察到你需要为你的应用提供什么。虽然您可以使用 ADB 工具从命令行启动几乎任何 Android 活动,但是您也可以以编程方式启动活动。后面的章节将使用这一概念来构建脚本以自动化多个活动。

同样的技术可用于启动 SL4A 来执行脚本,如下所示:

am start -a com.googlecode.android_scripting.action.LAUNCH_FOREGROUND_SCRIPT -n com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher -e com.googlecode.android_scripting.extra.SCRIPT_PATH “/sdcard/sl4a/scripts/hello_world.py"

在下一章中,我将向您展示如何使用 Eclipse 将解决方案自动部署到模拟器或目标设备并启动它。我发现最后一个非常有用的命令将在设备上启动一个私有服务器来启用远程调试:

adb shell am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER -n com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher

日志猫

logcat是 ADB 命令的名称,用于从仿真器或硬件设备转储或抓取当前日志文件。如果您在 Windows 的命令提示符下或者在 Linux 或 Mac OS X 的终端窗口中键入该命令,它将转储当前日志并继续显示新条目,直到您按 Ctrl+C 停止它。该工具将帮助您调试意外停止且没有错误的应用。它也可以从 Eclipse 窗口中获得,您将在下一章中看到。

达尔维克调试监控服务(DDMS)

DDMS 是 ADB 的补充工具,实际上使用 ADB 主机服务器到设备守护进程进行所有通信。如果您在不同的目录中使用 ADB 工具从 SDK 版本升级,这将变得非常明显。图 3-17 显示了当前连接了仿真器和物理设备的 DDMS 用户界面。

images

图 3-17。 DDMS 显示

DDMS 有许多你想了解更多的特色。一个是设备菜单中的文件浏览器工具。要浏览特定设备上的文件,请在 DDMS 应用的左上方窗格中选择该设备,然后打开文件浏览器。请注意,您将无法在普通设备上看到系统区域中的任何文件,因为它们受到保护。如果设备已经被根目录化,您将能够看到这些文件。术语root是指通过使用第三方应用或一些其他非重要方法获得对设备的 root 访问权限。大多数设备都将系统文件区设置为只读,因此用户不能进行任何更改。在仿真设备上允许完全浏览,看起来有点像图 3-18 。这里您可以看到联系人数据库,它实际上是一个 SQLite 数据库文件。

images

图 3-18。 DDMS 文件浏览器

您可以通过选择屏幕截图从 DDMS 设备菜单中截取任何已连接设备的屏幕截图。如果您正在编写关于特定应用的文档,并且需要包含屏幕图像,这个特性就非常方便。图 3-19 显示了一个使用 SL4A 发行版中hello_world.py文件的 DDMS 的示例屏幕截图。

images

图 3-19。 DDMS 截屏示例

在下一章中,我将向您展示这些工具如何直接与 Eclipse 集成,以提供您为 Android 编写、测试、调试和部署任何应用所需的一切。

总结

本章重点介绍了如何完全安装和配置 Android SDK,使其更易于在仿真和真实设备上开发和测试。这里讨论的许多工具和概念将在以后用于帮助简化开发过程,并且在某些情况下,自动化开发过程。下一章将介绍使用 Eclipse 和 Android 开发工具包(ADT)作为主要开发平台。

以下是本章的要点:

  • Android SDK :它真的很大,可能比你想象的要多,但是它确实有很多工具,比如 ADB、设备模拟器和 DDMS,这些都非常有用
  • Linux 命令行:如果你不习惯在 Linux 命令行或 Windows DOS 提示符下输入命令,你可能需要花些时间阅读一下这个主题。许多像 ADB 这样的工具使用命令行来完成任务,您也可以学习如何利用它们。
  • 使用模拟器:模拟器最大的好处是你可以删除它,然后重新开始。模拟器应该可以很好地处理您想要尝试的许多事情。在真实设备上尝试之前,最好在模拟器上测试一些东西。不要害怕删除一个模拟器,并重新开始,如果它变得非常慢或似乎不能正常工作。
  • 使用批处理/脚本文件:我做的第一件事就是在 Windows 上写几个批处理脚本来做一些事情,比如在设备上启动一个私有服务器,将文件复制到正确的目录。如果您正在进行任何数量的编码和测试,您会希望身边有一些这样的脚本。

四、将 Eclipse 用于开发

本章将带您完成 Eclipse 的安装、配置和开发。

images 注意虽然 Eclipse 可以在 Linux、Mac OS X 和 Windows 上工作,但本章将使用 Windows 作为主要的开发环境。

好吧。我们开始吧。以下是我将在本章中介绍的内容:

  • 在开发机器上安装 Eclipse
  • 使用 Eclipse 基础知识
  • 了解 Android 开发工具包
  • 使用 Pydev

通过使用这里给出的说明,您将能够在几分钟内安装好 Eclipse,并为生产工作做好准备。我将带您了解 Eclipse 本身以及许多其他插件的基本安装步骤,以帮助我们开发本书中的所有示例。

在这一点上需要指出的是,Eclipse 从一开始就是作为一个用 Java 语言编写的可扩展的集成开发环境(IDE)来开发的。因为它是用 Java 编写的,所以它自动地成为了一个跨平台的应用,事实上,这是从一开始就有的想法。Eclipse 已经发展成为一个庞大的项目,这一点经常会吓跑一些人。

在开发机器上安装 Eclipse

Eclipse 有多种风格。选择一个适合你的,在很大程度上是一个品味问题。如果你打算用它来完成本书中的练习,你可以下载经典版。在撰写本书时,当前版本是 3.6.1,代号为 Galileo。对于 Linux 和 Windows,您可以选择 32 位或 64 位下载。对于苹果电脑,你可以选择苹果 OS X(碳纤维)、苹果 OS X(可可 32)或苹果 OS X(可可 64)。选择与您的操作系统相匹配的一个。Eclipse 需要安装 Java 才能工作,所以如果你跳过了第二章中关于下载和安装 Java 的部分,你需要现在就回去做。Eclipse 发布在一个单独的.zip文件中,这意味着安装程序所要做的就是解压文件内容。在 Windows Vista 和 Windows 7 上,您可以双击.zip文件,Windows 资源管理器将打开,让您选择提取所有文件,如图 4-1 中的所示。

images

图 4-1。 Eclipse SDK 安装

一旦有了一个解压了所有 Eclipse 文件的目录,您应该能够通过双击Eclipse.exe文件在 Windows 中启动程序。你可能想在桌面上创建一个快捷方式,因为这将是一个经常使用的应用。记住使用右键单击 Create Shortcut Here 方法来确保 Eclipse 能够正常启动。第一次运行 Eclipse 时,你会看到一个类似于图 4-2 中的对话框屏幕,提示你选择存储项目文件的位置。这在 Eclipse 中称为工作区。您可以选择默认目录,如果您选中“将此目录用作默认目录”旁边的框并且不再询问,它将成为永久默认目录。如果不选择这个框,那么每次启动 Eclipse 时,都会提示您选择一个工作区。

images

***图 4-2。*选择工作区目录对话框

通过选择 Install New Software 选项,在 Eclipse 的 Help 菜单中安装插件模块。这将打开图 4-3 中所示的对话框。每个附加组件通常是从互联网上的默认存储库中安装的。这使得保持最新更新和安装最新版本变得更加容易。要添加新的存储库,请单击标记为“使用”的文本框旁边的“添加”按钮。您也可以在同一个文本框中输入 URL,Eclipse 安装程序将会到那个地址寻找附加包。图 4-3 显示了为 Eclipse 的 Android 开发工具(ADT)插件输入 URL 的结果。如果您使用 Add 按钮,您将有机会命名存储库。ADT 的 URL 是

[dl-ssl.google.com/android/eclipse](https://dl-ssl.google.com/android/eclipse)

您必须选择开发人员工具行下面的单个选项,或者只需选中开发人员工具旁边的复选框即可全选。一旦下载完成,您将被指示重启 Eclipse 以使更改生效。如果您不记得您安装了什么,只需选中“隐藏已安装的项目”旁边的框,您将看到所有新项目。如果您想查看已安装内容的列表,可以单击“已安装”链接。还有一个名为 Available Software Sites 的链接,它会将您带到一个预定义的附加站点列表。

images

图 4-3。 ADT 安装对话框

您想要安装的下一个附加组件是 Pydev。使用 Eclipse 和 Pydev 编写和调试 Python 代码是一种真正的集成开发体验。稍后我将带您了解如何使用 Pydev。现在,再次从帮助菜单打开软件更新屏幕。这次我们将为 Pydev 更新站点输入另一个 URL。它应该如下所示:

[Pydev.org/updates](http://Pydev.org/updates)

图 4-4 显示了进入pydev.org网址后你会看到的对话框。

images

图 4-4。 Pydev 安装对话框

本书练习所需的最后一个 Eclipse 插件是 Aptana Studio。它在编辑 HTML、CSS 和 JavaScript 方面大放异彩。使用与 ADT 和 Pydev 相同的过程,在标签 Work with 旁边的文本框中输入以下行:

[download.aptana.com/tools/studio/plugin/install/studio](http://download.aptana.com/tools/studio/plugin/install/studio)

这将在可用站点列表中创建一个新条目,并显示另一个对话框,您可以在其中选择并下载加载项。

定期检查 Eclipse 和附加组件的更新是一个好主意。您可以通过从“帮助”菜单中选择“检查更新”来完成此操作。如果发现任何更新,您将会看到一个类似于图 4-5 中的对话框。这个特别的更新是针对 Ubuntu 系统上的 Pydev 的。

images

***图 4-5。*可用更新对话框

Eclipse 基础知识

Eclipse 是一个巨大的应用,具有许多功能和选项。整本书都是关于使用 Eclipse 开发复杂应用的。第一次启动 Eclipse 时,你会看到一个欢迎屏幕,如图 4-6 所示。教程和示例项目是专门针对 Java 开发人员的。尽管阅读它们不会伤害你,但你可能想暂时跳过它们,因为它们并不真正适用于手头的主题。

images

***图 4-6。*月食欢迎画面

如果单击 Overview 项,将会打开另一个屏幕,其中有四个主题:工作台基础、Java 开发、团队支持和 Eclipse 插件开发。对本书来说,唯一真正重要的是工作台基础知识主题。点击此项将启动 Eclipse 帮助系统,如图图 4-7 所示。如果您展开入门部分,您会发现一个详细的教程,其中涵盖了浏览 Eclipse 需要知道的所有基本操作。如果您以前没有使用过 Eclipse,那么花时间研究一下教程材料是非常值得的。

Eclipse 使用许多基本概念和术语来处理程序的不同功能和操作。工作台是用来标识 Eclipse 应用的整个窗口的术语。工作台窗口通常包含多个带有编辑器和视图的子窗口,例如 Project Explorer。当您打开其他窗口时,它们将包含编辑器或视图。Eclipse 还支持窗口中的多个选项卡,因此您可以在同一个编辑器窗口中打开多个文件。使用帮助菜单可以随时导航回欢迎页面。

images

***图 4-7。*月食帮助屏幕

视角

从程序使用者的角度来看,视角的概念更容易理解。每个人都会把自己的观点或偏好带到任何活动中。透视图实际上只不过是菜单和窗口在任何时候都打开的个人偏好。Eclipse 对透视图的定义是“一组视图和编辑器(部件)的可视容器”([www.eclipse.org/articles/us…](http://www.eclipse.org/articles/using-perspectives/PerspectiveArticle.html))。Eclipse 附带了许多已经为典型用途配置的透视图,比如编写代码。当您的活动发生变化时,您会切换到不同的视角,例如当您从编码切换到调试时。图 4-8 显示了一个典型的 Pydev 透视图。

images 注意高分辨率的大屏幕显示器提供了大量的空间来运行多窗口 Eclipse。您还可以在多个监视器上运行 Eclipse,并使用分离特性将单个窗口移出工作区容器,移到不同的监视器上。

images

图 4-8。 Pydev 透视图工作区

视角可以最大程度地定制。如果您不喜欢某个工具栏,可以将其关闭。每个透视图将特定的窗口加载到屏幕上的默认位置。您可以随意移动这些窗口,根据自己的喜好进行排列。要定制当前透视图,右键单击工作区右上角的名称/图标,并选择 customize。您将看到如图图 4-9 所示的屏幕。几个选项已经展开,以显示选择之间的差异。

此对话框使您可以完全控制当前透视图的每个方面。如果该屏幕中某个项目旁边的复选框有复选标记,则意味着该项目下的每个选项也被选中。填充复选框的蓝色方框表示该项下的某些选项已被选中,而某些选项未被选中。如果您展开每一项,您会很快看到 Eclipse 有很多选项。在这一点上,最好的方法是调整那些你认为会帮助你更有效率的事情,剩下的就不要管了。

附加选项卡允许您自定义工具栏可见性、菜单可见性(或显示在主工作区窗口顶部的项目)、命令组可用性以及将显示为子菜单项的快捷方式。“按命令组过滤”复选框将打开或关闭对话框左侧的另一个项目列表,显示出现在不同命令组中的选项。

images

***图 4-9。*自定义透视屏幕

您可以通过单击右上角带有小加号的看起来像窗口的小图标来快速打开一个新的透视图。它位于屏幕顶部的当前视角指示器旁边。(参见图 4-8 。)这将弹出如图图 4-10 所示的打开透视图对话框,其中列出了所有可用的透视图。单击任何项目都会打开该透视图及其所有窗口和视图。

images

图 4-10。 Eclipse 打开透视图对话框

您可以随时改变主意,通过单击 Cancel 按钮返回到当前打开的透视图。

项目

项目是收集一个特定应用的所有活动部分的地方。一个项目可以仅仅是一个单独的源文件或者任意数量的不同文件和文件类型。使用文件images新建项目或 Alt+Shift+N 组合键创建一个新项目。在 Mac 上,你可以使用 Command+Shift+N。这两种方法都会启动新项目向导(参见图 4-11 )。

选择 General Project 将启动另一个对话框,提示您命名项目,然后在默认工作区目录下创建一个同名的空文件夹。稍后我们将介绍如何创建 Pydev 项目。

images

图 4-11。 Eclipse 新项目向导

Android 开发工具包

Google 已经尽一切努力来简化为 Android 平台开发应用的过程,包括为 Eclipse 提供 Android 开发工具包(ADT)扩展。创建新项目时的一个选项是 Android Project。这是探索在 Android 平台上使用 Java 开发原生应用的好方法。图 4-12 显示了新的 Android 项目向导创建了一个名为 MyTestApp 的新项目,该项目基于样本记事本应用。ADT 附带了许多示例应用,以展示如何利用 Android 平台的不同特性。

images

***图 4-12。*新 Android 项目向导

当您单击 Finish 按钮时,向导将创建新项目,并将所有示例源代码复制到工作目录中。图 4-13 显示了这个项目在编辑器中的样子。

images

***图 4-13。*用新的 Android 项目向导创建的 MyTestApp】

此时,您可以单击 Run 图标,Eclipse 将完成编译和部署过程。如果您已经在开发机器上配置了模拟器,它将启动模拟器并将应用推送到设备上。图 4-14 显示了在通用仿真器上运行的应用。这个过程的美妙之处在于使用 Eclipse 和 ADT 可以快速编译、部署和测试序列。目标是让同样的体验成为可能,但是使用 Python 和 SL4A。

images 提示在 Eclipse 中,有多种方法可以完成相同的任务。如果你更喜欢点击鼠标,你会发现你需要执行的任何操作都有图标。如果你更喜欢键盘,你也会发现这些。您可以使用本章前面提到的“自定义透视图”对话框来探索不同的选项。

images

***图 4-14。*运行在通用仿真器上的 MyTestApp】

其中一个可用的视角被标记为 DDMS。图 4-15 显示了 MyTestApp 在模拟器中运行时的透视图。这个透视图给你提供了在第三章中讨论的 DDMS 工具的完整功能。此透视图打开的窗口包含设备;仿真器控制;Logcat 以及一个选项卡式窗口,可以快速访问线程、堆、分配跟踪器和文件资源管理器。

要查看与 MyTestApp 相关的信息,您必须向下滚动到 Devices 窗口的底部并找到该应用。在这种情况下,它被命名为com.example.android.notepad。当你选择这一行时,你将能够看到你曾经想知道的关于一个 Android 应用的一切――以及更多。

images

图 4-15。【MyTestApp 运行时的 DDMS 视角

在讨论多监视器时,前面提到的一个特性是能够“撕下”任何窗口,并将其拖出主 Eclipse 窗口。图 4-16 显示了仿真器控制窗口被自己移出后的样子。为此,在窗口标题下左键单击并按住;然后将鼠标拖离 Eclipse 主窗口。如果主窗口最大化,这将不起作用。要关闭窗口,只需单击窗口右上角的 X。要在 Eclipse 中重新打开窗口,请打开窗口菜单。然后从显示视图中,选择要打开的窗口。如果您之前将它从 Eclipse 中分离出来,它将恢复脱离主窗口。您可以使用与取消停靠时相同的方法将它拖回主窗口。如果你只是想回到任何视角的默认设置,在窗口菜单上有一个重置视角选项可以让你回到最初的起点。

images

***图 4-16。*与主 Eclipse 窗口分离的仿真器控制窗口

使用 Pydev

Pydev 是一个 Eclipse 插件,它的创建是为了让 Python 开发人员的生活更加轻松。它有许多专门针对 Python 语言的特性,还有快捷方式、模板和它自己的默认透视图。Pydev 最重要的部分可能是它是由 Python 开发者为 Python 开发者创建的。如果你在任何平台上开发 Python 代码,这是你不想离开家不带的工具之一。

Pydev 工作之前必须做的一件事是配置 Python 安装的位置。图 4-17 显示了从首选项窗口展开的 Pydev 菜单项。您可以在工作区窗口顶部列表的窗口菜单下找到首选项选项。选择解释器- Python 菜单项最初会显示一个空白窗口。这需要配置为指向您的 Python 2.6 解释器安装目录。在 Windows 上,这通常位于路径C:\Python26中。在 Ubuntu 10.10 和大多数其他基于 Debian 的发行版上,你会在/usr/bin/python中找到 Python 可执行文件。

images

***图 4-17。*配置 Python 解释器的首选项对话框

安装 Pydev 后,此时您有两个选择:新建 Pydev 项目和新建项目。图 4-18 显示了新的 Pydev 项目屏幕的样子。此对话框中有许多必须设置的项目。在对话框的顶部,您必须输入新项目的名称,该名称将用作默认工作区下的子目录的名称,以及将在项目浏览器窗口中显示的标题。接下来,您必须使用下拉框更改语法版本,并选择 2.6。最后,您应该将解释器设置为指向 Python26 目录,以防您碰巧安装了多个版本的 Python。

images

***图 4-18。*新 Pydev 项目对话框

一旦创建了新项目,您就可以开始输入代码了。Pydev 使用模板来帮助您使用预定义的源代码快速构建代码。您可以使用任何提供的模板或创建自己的模板。您可以在这里放置一些标准的标题注释,包括版权信息、您的姓名以及其他任何可能相关的内容。

如果你想查看所有默认的快捷键,你可以按 Ctrl+Shift+L。这将弹出一个当前定义的所有快捷键的可滚动窗口,如图图 4-19 所示。就像 Eclipse 中的其他东西一样,您可以从 Preferences 对话框中定义自己的快捷键。如果屏幕上有快捷方式列表,您可以通过再次按 Ctrl+Shift+L 直接跳转到该页面。

images

***图 4-19。*快捷显示窗口示例

Pydev 附带了许多预定义的模板,可以在创建新的源文件时使用,或者快速插入到代码窗口中。当您使用 File images New images Pydev 模块过程创建新的源文件时,您将有机会使用模板文件。这将打开如图图 4-20 所示的对话框。如果打开了编辑器窗口,可以使用 Ctrl+space 组合键访问模板。这将打开一个窗口,其中列出了可供您插入的潜在代码片段。当任何项目高亮显示时,按回车键将在该点插入代码。

如果您第二次按 Ctrl+space,您将看到此时适用的可用模板列表。这将切换当前列表以显示可用的模板,并在第二个窗口中显示如果您点击 Return 将插入的语句的视图。可通过创建新 Python 模块对话框中显示的配置链接创建新模板。单击此链接将打开首选项对话框,并选择模板选项。

images

***图 4-20。*新 Pydev 模块对话框

要定义一个新模板,点击新建按钮并完成图 4-21 所示的对话框。这将允许您在每个 Python 模块的开头创建相同的基本代码时节省击键次数。“插入变量”按钮允许您根据某些变量(如文件路径、文件或模块名称、日期、时间或当前选定的单词)插入文本。

选择模板时,将插入图案窗口中的文本。在这种情况下,您将看到几乎每个 SL4A 应用开头都会出现的代码。Android 模块包括使用远程过程调用(RPC)机制在 SL4A 应用和底层本机 API 之间进行通信的代码。在这种情况下,RPC 调用与伪代理一起使用,在底层 Android 操作系统之间传递信息。这提供了一个额外的安全层,以防止流氓应用在您的设备上做一些邪恶的事情。

images

图 4-21。 Pydev 新模板对话框

您必须使用 ADB 工具来将 SL4A 应用部署到仿真器或真实设备上。这可以从命令行完成,或者您可以使用少量代码自动完成该过程:

`#!/usr/bin/env python

import subprocess

ADB = r*'C:\Program Files (x86)\Android\android-sdk-windows\platform-tools\adb.exe'* APPLICATION = 'hello_world.py' TARGET = '/sdcard/sl4a/scripts/'

def main():

Upload the application.

subprocess.call([ADB, '-e', 'push', APPLICATION, TARGET + APPLICATION])

Launch the application.

subprocess.call(*'"%s" -e shell am start * *-a com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT * *-n * *com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher * *-e com.googlecode.android_scripting.extra.SCRIPT_PATH * "%s%s"' % (ADB, TARGET, APPLICATION))

if name == 'main': main()`

如果您用这段代码创建一个文件,将它包含在您的 Pydev 项目中,并在准备好测试时运行它,它将为您完成工作。一个好的描述性名字,比如Launch_app.py,会在需要使用的时候帮助你识别它。将它保存到工作区的一个目录中,这样您就可以将它复制到每个项目中。您唯一需要更改的是下面这一行:

APPLICATION = 'hello_world.py'

这显然会因每个应用而异,并且应该与存储在磁盘上的文件名相匹配。Pydev 提供了一个完整的调试工具,允许你设置断点,检查变量,单步调试代码行,如图 4-22 所示。在编辑器窗口中查看代码时,可以使用鼠标右键单击“添加断点”来设置断点。一旦设置了断点,就可以修改其属性并添加条件。当你有一个很长的循环并且你想在一些迭代之后中断时,这是非常宝贵的。现在,当您运行应用时,一旦遇到断点并满足条件,就会提示您。

images

图 4-22。 Pydev 应用调试

在真实或模拟设备上执行 Eclipse 中的代码的另一种方法是使用 ADB 工具来建立设备的代理。这在第三章中已经介绍过了,但是我将在这里再次介绍这些步骤,向您展示如何在 Eclipse 中完成。要在真实设备上实现这一点,您需要使用 USB 电缆连接它,并在设备上配置适当的设置,如启用 USB 调试和启用除仅充电之外的其他设置。接下来,您必须启动 SL4A 并从解释器菜单启动一个服务器。最后,您必须注意从设备上的通知页面分配给服务器的 TCP 端口。最后,但同样重要的是,您必须以管理员权限在命令窗口中键入以下内容:

adb forward tcp:9999 tcp:50681

这指示 ADB 将所有 TCP 流量从本地端口 9999 转发到远程端口 50681。完成所有这些后,您现在应该能够从 Eclipse 运行您的 Python 代码了,所有对 Android API 的调用都将通过代理发送到设备。从 Eclipse 内部运行的好处是,您可以看到 DDMS 中的所有调试信息和所有控制台输出,包括从设备返回的任何错误消息。

我发现一个非常有价值的工具是文件比较工具。要使用它,首先在工作区窗口左侧的 navigator 窗格中选择两个文件,方法是左键单击第一个文件,然后按住 Shift 左键单击第二个文件。接下来,使用鼠标右键操作并选择与images相互比较。这将产生一个双窗格窗口,突出显示任何差异,如图 4-23 所示。

images

***图 4-23。*文件比较工具

在 Eclipse 中使用多种文件类型

Eclipse 真正擅长的事情之一是管理具有多种文件类型的复杂应用。第九章将详细讨论 SMS Sender 程序,但现在它将作为一个例子,展示如何创建一个包含许多不同文件的项目。这可以从头开始或从现有文件中完成。在这种情况下,我们将使用现有的文件并将它们添加到一个新的 Pydev 项目中。

您必须做的第一件事是创建一个新的 Pydev 项目。这将在您的工作区下创建一个新文件夹和一个单独的src目录。要将文件添加到项目中,您可以使用文件菜单中的导入工具,也可以将文件从文件管理器(如 Windows 上的 Windows 资源管理器或 Linux 上的 Nautilus)拖放到项目中。图 4-24 显示了选择导入方法时将看到的对话框。

images

***图 4-24。*导入文件工具

当你点击下一步按钮时,你会看到另一个对话框,允许你浏览你想要导入的目录(见图 4-25 )。这应该是顶层或根目录,其中包含您希望导入到该目录或子目录中的所有文件。

images

***图 4-25。*导入目录选择器对话框

如果您选择拖放方法,您需要在文件管理器中选择文件和目录,然后将它们拖到 Eclipse 窗口上,并在指向项目时释放鼠标。这将弹出一个新的对话框,允许你选择如何处理这些文件(见图 4-26 )。

images

***图 4-26。*文件和文件夹操作对话框

一旦导入完成,您将拥有一个新的 Pydev 项目。图 4-27 显示了 SMSSender 项目,其中的文件是使用拖放方法包含的。左侧窗格显示了一个目录树,其中包含与此项目相关的所有文件。您可以并且应该在项目中包含所有文件,甚至是那些您不会用 Eclipse 编辑的文件,比如图像,以简化以后的部署过程。构建一个用于分发的包需要所有文件都包含在您的项目中,所以您最好现在就学习如何做。这还包括您的项目所依赖的任何库文件,如 JavaScript 库。

images

***图 4-27。*使用拖放方法创建的多文件示例

如果您双击项目中的一个非文本文件,Eclipse 将尝试使用默认的查看器应用打开该文件。如果你右击一个文件,你会看到包括打开方式在内的选项。图 4-28 显示了打开的选项菜单。

images

***图 4-28。*打开文件选项菜单

另一个选项将打开另一个对话框(见图 4-29 ),其中有一长串默认编辑器。如果您选择外部程序单选按钮,您将获得系统上所有已注册 mime 类型的列表。

images

***图 4-29。*编辑选择菜单

Eclipse 在为文件类型提供正确的编辑器方面做得很好,你可以从图 4-27 中的 HTML 标签看到这一点。

总结

Eclipse 是一个强大的 IDE。它也是一个复杂的软件应用,对于新手来说可能有些令人生畏。本章重点介绍了配置 Eclipse 并使用它快速有效地构建和测试您的应用。希望您已经看到了 Eclipse、Pydev 和 ADT 的组合可以做些什么。

以下是关于 Eclipse 需要记住的一些事情:

  • 不要害怕 Eclipse :许多人因为听说过或读到过 Eclipse 有多么庞大和笨重而回避使用它。虽然它可能在内存有限的旧计算机上运行缓慢,但它在典型的现代工作站上确实运行良好。
  • 阅读文档:如果你以前没有使用过 Eclipse,浏览一下文档也是个不错的主意。至少看看最近的 Eclipse 教程,帮助您快速入门。
  • 找到你的插件:除了本章提到的插件,还有很多插件。如果你认为还有其他需要的东西,浏览一下网站,在谷歌上搜索一下。Aptana Studio 并不是唯一的 HTML 编辑器,所以您可以随意查看其他一些选项。
  • 学习快捷键:学习几个键盘快捷键可以节省你很多时间,有助于让你的手保持在键盘上。每次你不得不移动你的手到鼠标上,不仅花费时间,而且潜在地增加了你的疲劳系数。

五、探索 Android API

本章将深入研究 Android 应用编程接口(API ),并展示如何使用 Python 调用不同的函数。

images 注意 Python 无法通过 SL4A 的 r3 版本访问每一个 Android API。其他语言如 BeanShell 确实有这种能力,并且将用于查看一些缺失的功能。

本章将在很大程度上依赖于对第一章中介绍的概念的基本理解。主题包括 Android 活动和意图、JSON、Python 类型和 RPC。我将带您浏览每个 API facade,并在适当的地方包括如何使用它们的例子。它们都有自己的描述性名称,比如cameraCapturePicturerecognizeSpeech

所有与 Android API 的双向通信都使用 JSON 作为传递数据的底层结构。如果你跳过了 JSON 的部分,你可能想回到第一章并阅读这一部分。JSON 并不复杂,但是如果您不知道自己在看什么,它可能会有些混乱。Python 很好地处理了 JSON,甚至有一个内置的过程pprint,可以很好地打印 JSON 结构。图 5-1 显示了不使用 pretty print 例程时 API 调用getLaunchableApplications的返回结果。

images

***图 5-1。*getLaunchableApplications API 调用的 JSON 返回示例

图 5-2 显示了相同的结果,但是使用了pprint模块,以一种更加易读的形式。

images

***图 5-2。*使用 pprint 格式化的 getLaunchableApplications JSON 示例

另一个概念,我假设你在这一点上理解是一个 Android 活动。SL4A 提供了一个启动并忘记(或启动并等待)Android 活动完成的接口。

探索 Android APIs

在 Python 中,所有 SL4A API 调用都返回一个包含三个字段的对象:

  • id:与 API 调用相关的严格递增的数字 ID
  • result:API 调用的返回值,如果没有返回值则为null
  • error:对发生的任何错误的描述,如果没有发生错误,则为null

android.py文件用三种方法定义了一个 Android 类。通过研究_rpc方法,可以了解 API 请求是如何使用 RPC 调用和 JSON 传递给底层操作系统的:

`def _rpc(self, method, *args): data = {'id': self.id, 'method': method, 'params': args} request = json.dumps(data) self.client.write(request+'\n') self.client.flush() response = self.client.readline() self.id += 1 result = json.loads(response) if result['error'] is not None: print result['error']

namedtuple doesn't work with unicode keys.

return Result(id=result['id'], result=result['result'], error=result['error'], )`

同样的基本概念也适用于其他语言。在 BeanShell 中,代码如下所示:

call(String method, JSONArray params) { JSONObject request = new JSONObject(); request.put("id", id); request.put("method", method); request.put("params", params); out.write(request.toString() + "\n"); out.flush(); String data = in.readLine(); if (data == null) { return null; } return new JSONObject(data); }

安卓外观

第一章讨论了 SL4A 用来向底层 Android API 传递信息的 RPC 机制的基础知识。每个支持的 API 函数在每种 SL4A 语言中都有一个对应的接口,称为 facade ,带有 API 所需的适当参数。这些参数中有些是强制性的,有些是可选的。表 5-1 显示了顶层立面以及它们提供的访问功能。附录 A 包含所有 SL4A API 调用的完整列表。

***表 5-1。*安卓 API 外观

| `ActivityResultFacade` | 设置活动的返回值 | | `AndroidFacade` | 常见的 Android 功能 | | `ApplicationManagerFacade` | 获取有关已安装应用的信息 | | `BatteryManagerFacade` | 公开电池管理器 API | | `BluetoothFacade` | 允许访问蓝牙功能 | | `CameraFacade` | 所有与摄像机相关的操作 | | `CommonIntentsFacade` | 通用 Android 意图 | | `ContactsFacade` | 提供对联系人相关功能的访问 | | `EventFacade` | 公开作为 RPC 从事件队列读取的功能和作为纯 Java 函数写入事件队列的功能 | | `EyesFreeFacade` | 为 API 3 或更低版本提供文本到语音(TTS)服务 | | `LocationFacade` | 暴露与`LocationManager`相关的功能 | | `MediaPlayerFacade` | 展示基本的`mediaPlayer`功能 | | `MediaRecorderFacade` | 记录媒体 | | `PhoneFacade` | 暴露`TelephonyManager`功能 | | `PreferencesFacade` | 允许访问`Preferences`界面 | | `SensorManagerFacade` | 暴露与`SensorManager`相关的功能 | | `SettingsFacade` | 公开电话设置功能 | | `SignalStrengthFacade` | 显示信号强度功能 | | `SmsFacade` | 提供对 SMS 相关功能的访问 | | `SpeechRecognitionFacade` | 包含与 Android 语音转文本功能相关的 RPC 实现 | | `TextToSpeechFacade` | 为 API 4 或更高版本提供 TTS 服务 | | `ToneGeneratorFacade` | 产生双音多频音 | | `UiFacade` | 创建和处理对话框中的信息 | | `WakeLockFacade` | 展示了`PowerManager`的一些功能(特别是唤醒锁) | | `WebCamFacade` | 从前置摄像头捕捉视频 | | `WifiFacade` | 管理 WiFi 无线电的所有方面 |
活动结果面

这个 facade 提供了一种机制来显式地设置脚本如何将信息作为活动返回。每当使用 Android API 调用startActivityForResult()启动脚本 APK 时都会用到它。使用这种方法意味着您的脚本将返回某种结果,设置结果的类型(resultValue)和RESULT_CANCELED (0)或RESULT_OK (-1)是很重要的。

雄激素

这个外观有点包罗万象,提供了 Android 操作系统(OS)的许多可用功能。有检查当前执行包版本号(getPackageVersiongetPackageVersionCode)和 SL4A 版本(requiredVersion)的功能。第二个提供了一个很好的机制,在您的代码需要一些特定于版本的特性时,可以检查 SL4A 的最低版本。

这个 facade 里有几个弃用的调用,包括getInputgetPassword。两者都被更新的 Android API 调用所取代,但是仍然支持旧的脚本。图 5-3 显示了如果你使用一个废弃的 API 调用,你会看到什么。

images

***图 5-3。*不推荐使用的 API 调用的通知消息

每当您使用不推荐使用的函数时,SL4A 都会在您的通知窗口中添加一条消息。您将在这里找到启动 Android 活动并等待结果的函数,或者只是启动并返回。像 Windows 和 Linux 一样,Android 支持剪贴板的概念,用于在应用之间复制和粘贴信息。您可以使用函数getClipboardsetClipboard functionsfunction 从脚本中完成此操作。

lognotify功能提供了显示(notify)或保存(log)信息的方法,以便使用 logcat 应用查看。还有一个常用的makeToast功能,它只是在设备屏幕上短暂闪烁一条信息,然后删除它。如果您希望通过振动设备引起用户的注意,您可以使用vibrate功能。sendEmail功能将启动一个发送电子邮件的活动(该活动将取决于您在设备上加载的能够发送电子邮件的应用)并填充recipientsubjectbodyattachment字段。您将不得不使用该应用来实际发送消息。在下一章中,我将向您展示另一种不需要外部活动就能发送电子邮件的方法。

在引言中,我解释了 Android 架构,以及活动如何适应不同应用的执行。SL4A 的早期版本包括启动活动(startActivity)和启动活动并等待结果(startActivityForResult)的能力。SL4A r4 引入了两个额外的函数,允许您使用意图(startActivityIntent)启动活动,以及使用结果意图(startActivityForResultIntent)启动活动。SLA r4 中的另一个新函数调用是makeIntent。需要此函数来创建一个 intent,供需要 intent 的任何一个startActivity调用使用。这个函数的返回是一个表示意图的对象。

SL4A r4 还在 Android facade 中引入了getConstants函数,帮助您确定特定 Android 类中有哪些常量可用。当您想要查询内容供应器,但是不知道有什么可用时,这个函数非常方便。下面的一行代码演示了如何使用该调用来显示联系人提供程序中可用的常量:

res=droid.getConstants("android.provider.ContactsContract$CommonDataKinds$Phone").result

Android 2.2 将从联系人提供程序返回总共 99 个常量。以下是一些常量的简短列表:

{u'AGGREGATION_MODE': u'aggregation_mode', u'AVAILABLE': 5, u'AWAY': 2, u'CONTACT_ID': u'contact_id', u'CONTACT_PRESENCE': u'contact_presence', u'CONTACT_STATUS': u'contact_status', u'CONTACT_STATUS_ICON': u'contact_status_icon', u'CONTACT_STATUS_LABEL': u'contact_status_label', u'CONTACT_STATUS_RES_PACKAGE': u'contact_status_res_package', u'CONTACT_STATUS_TIMESTAMP': u'contact_status_ts', u'CONTENT_FILTER_URI': u'content://com.android.contacts/data/phones/filter', u'CONTENT_ITEM_TYPE': u'vnd.android.cursor.item/phone_v2', u'CONTENT_TYPE': u'vnd.android.cursor.dir/phone_v2', u'CONTENT_URI': u'content://com.android.contacts/data/phones', u'CUSTOM_RINGTONE': u'custom_ringtone', u'DATA': u'data1', u'DATA1': u'data1', u'DATA2': u'data2', u'DATA_VERSION': u'data_version', u'DELETED': u'deleted', u'DISPLAY_NAME': u'display_name', u'DISPLAY_NAME_ALTERNATIVE': u'display_name_alt', u'DISPLAY_NAME_PRIMARY': u'display_name', u'DISPLAY_NAME_SOURCE': u'display_name_source', u'DO_NOT_DISTURB': 4, u'HAS_PHONE_NUMBER': u'has_phone_number', u'IDLE': 3, u'INVISIBLE': 1, u'IN_VISIBLE_GROUP': u'in_visible_group', u'IS_PRIMARY': u'is_primary', u'LAST_TIME_CONTACTED': u'last_time_contacted', u'LOOKUP_KEY': u'lookup', u'MIMETYPE': u'mimetype', u'NAME_RAW_CONTACT_ID': u'name_raw_contact_id', u'NAME_VERIFIED': u'name_verified', u'NUMBER': u'data1', u'_COUNT': u'_count', u'_ID': u'_id'}

同样的getConstants函数可以用来获得android.content.Intent中所有可用常数的列表。这将包括所有标准的 Android 意图。下面是将列表打印到控制台的一小段代码:

import android droid = android.Android() myconst = droid.getConstants("android.content.Intent").result for c in myconst: print c,"=",myconst[c]

运行此代码的结果将产生一个格式良好的列表,如下所示:

ACTION_AIRPLANE_MODE_CHANGED = android.intent.action.AIRPLANE_MODE ACTION_ALARM_CHANGED = android.intent.action.ALARM_CHANGED ACTION_ALL_APPS = android.intent.action.ALL_APPS ACTION_ANSWER = android.intent.action.ANSWER ACTION_APP_ERROR = android.intent.action.APP_ERROR ACTION_ATTACH_DATA = android.intent.action.ATTACH_DATA ACTION_BATTERY_CHANGED = android.intent.action.BATTERY_CHANGED ACTION_BATTERY_LOW = android.intent.action.BATTERY_LOW ACTION_BATTERY_OKAY = android.intent.action.BATTERY_OKAY ACTION_BOOT_COMPLETED = android.intent.action.BOOT_COMPLETED ACTION_BROADCAST_KEYEVENT = android.intent.action.BROADCAST_KEYEVENT ACTION_BROADCAST_MOTIONEVENT = android.intent.action.BROADCAST_MOTIONEVENT ACTION_BROADCAST_TRACKBALLEVENT = android.intent.action.BROADCAST_TRACKBALLEVENT ACTION_BUG_REPORT = android.intent.action.BUG_REPORT ACTION_CALL = android.intent.action.CALL ACTION_CALL_BUTTON = android.intent.action.CALL_BUTTON ACTION_CALL_EMERGENCY = android.intent.action.CALL_EMERGENCY ACTION_CALL_PRIVILEGED = android.intent.action.CALL_PRIVILEGED ACTION_CAMERA_BUTTON = android.intent.action.CAMERA_BUTTON ACTION_CHECK_CONTACT_DB_CORRUPT = android.intent.action.ACTION_CHECK_CONTACT_DB_CORRUPT ACTION_CHOOSER = android.intent.action.CHOOSER ACTION_CLOSE_SYSTEM_DIALOGS = android.intent.action.CLOSE_SYSTEM_DIALOGS ACTION_CONFIGURATION_CHANGED = android.intent.action.CONFIGURATION_CHANGED ACTION_CONTACTS_CHANGE = anddroid.intent.action.CONTACTS_CHANGE ACTION_CONTACTS_DB_READY = android.intent.action.CONTACTS_DB_READY ACTION_CONTACT_DATABASE_CORRUPT = android.intent.action.CONTACT_DB_CORRUPT ACTION_CREATE_SHORTCUT = android.intent.action.CREATE_SHORTCUT ACTION_DATE_CHANGED = android.intent.action.DATE_CHANGED ACTION_DEFAULT = android.intent.action.VIEW ACTION_DELETE = android.intent.action.DELETE ACTION_DELETE_THREAD_MSG = android.intent.action.DELETE_THREAD_MSG ACTION_DEVICE_STORAGE_LOW = android.intent.action.DEVICE_STORAGE_LOW ACTION_DEVICE_STORAGE_OK = android.intent.action.DEVICE_STORAGE_OK ACTION_DIAL = android.intent.action.DIAL ACTION_DIALER_NEED_CHANGE = android.intent.action.DIALER_NEED_CHANGE ACTION_DOCK_EVENT = android.intent.action.DOCK_EVENT ACTION_EDIT = android.intent.action.EDIT

有了这些信息,你就可以使用makeIntent功能和startActivityForResultIntent来访问隐藏在 Android 操作系统深处的几乎任何功能。这里有一小段使用这种技术来显示您的通话记录:

import android droid = android.Android() myconst = droid.getConstants("android.provider.CallLog$Calls").result calls=droid.queryContent(myconst["CONTENT_URI"],["name","number","duration"]).result for call in calls: print call

注意,这段代码首先使用getConstants函数来确定CONTENT_URI的值,然后使用queryContent(ContactsFacade的一部分)调用来实际返回结果。

应用管理学院

这个 facade 中的四个函数可以列出所有可用的和正在运行的包,启动一个活动,或者强制停止一个包。您可以使用这些调用来编写自己的任务管理器或终止一组特定的包。请注意,getLaunchableApplications调用可能需要一段时间返回结果,这取决于您在设备上加载的应用数量。图 5-1 显示了原始 JSON 格式的部分应用列表,而图 5-2 显示了使用pprint函数格式化的相同列表。

电池组管理学院

任何与你的设备电池有关的东西都在这里。这个门面是一个谈论监控概念的好地方。在许多其他情况下,为了收集有意义的数据,您必须启动和停止对某种类型信息的监控。图 5-4 展示了一个使用这些 API 调用的交互式会话的例子。

这也是指出每个 API 调用返回的信息中的一些差异的好地方。Python IDLE 工具使得从工作站键盘舒适地探索不同的调用变得非常容易。这是假设您已经在设备上启动了 SL4A,启动了一个服务器,并使用 ADB 连接到它(如果这些都没有意义,请参见第二章)。在接下来的例子中,你会看到三个箭头,如>>>所示,表示来自 IDLE 的提示。如果您想自己尝试代码,请不要键入这些内容。

正如第一章中提到的,Python 中的一切都是对象。API 调用的每个返回都是一个结果对象。如果您检查来自_rpc方法的最后一行,您将看到以下内容:

return Result(id=result['id'], result=result['result'], error=result['error'], )

要访问 Python 调用的结果,可以将它赋给一个变量,然后计算结果,如下所示:

`>>> apps = droid.getLaunchableApplications()

pprint.pprint(apps.result)`

要确定 Python 中对象的类型,可以使用type()函数,如下所示:

`>>> type(apps) <class 'android.Result'>

type(apps.result) <type 'dict'>`

这表示apps是从类android.Result派生的对象。下面一行显示了apps.result的类型是dict,这在 Python 中实质上是一个键/值对。在 Java 中,这将被表示为一个Map对象。图 5-4 显示了检查不同电池管理 API 调用返回的结果。

images

***图 5-4。*电池管理 API 调用示例

蓝星脸

Android 设备拥有广泛的蓝牙功能,这可能是你在移动设备上想不到的。BluetoothFacade提供对所有这些功能的访问,从基本的连接特性到发送和接收 ASCII 和二进制数据。最简单的层次是用于控制连接的bluetoothAcceptbluetoothConnectbluetoothMakeDiscoverablebluetoothStop。您还可以使用checkBluetoothStatetoggleBluetoothState来简单地打开和关闭蓝牙无线电,或者只是检查它处于什么状态。虽然toggleBluetoothState功能听起来像是简单地翻转蓝牙无线电的当前状态,但它实际上会用一个可选参数将其设置为您想要的状态。默认情况下,这将在设备上弹出一个请求许可的屏幕,如图图 5-5 所示。

images

***图 5-5。*蓝牙 API 提示权限

BluetoothFacade还支持与设备之间的数据传输。这里的选项包括发送/接收 ASCII 字符的bluetoothReadbluetoothWrite。还有一个bluetoothReadLine可以读取整行文本。发送和接收二进制数据有bluetoothWriteBinarybluetoothReadBinary。这两个功能使得使用蓝牙向/从您的设备传输二进制文件成为可能。

照相机

当你从一个剧本中拍摄一张照片时,你基本上有两种选择。你可以抓拍相机当前正在拍摄的任何东西(cameraCapturePicture),或者启动图像捕捉应用(cameraInteractiveCapturePicture)。这是严格用于使用设备背面的镜头。对于带有前置摄像头的设备,有WebCamFacade。应该注意,这两个 API 调用都要求您在设备上传递一个路径来存储图像。如果您不熟悉如何访问不同的目录,您应该花些时间浏览您的设备。在大多数设备上,通常会有一个名为sdcard的可移动设备。安卓相机应用在/sdcard/DCIM/100Media存储图片。

顺便说一下,你应该知道 Android 有一个媒体扫描仪应用,它可以查找特定的文件类型,例如图片的.jpg,并将这些图像添加到默认应用的浏览列表中,例如 Gallery。如果您不希望这种情况发生,您可以使用带有前导句点的隐藏目录,例如/sdcard/.donotscan。你也可以添加一个名为.nomedia,的文件,Android 应该会忽略该目录中的媒体文件。

普通意向下降

Android 操作系统的 2.x 版本通过CommonIntentsFacade提供了一组通用意图。对于扫描条形码,有scanBarcode功能。底层代码将试图解释您正在扫描的内容,然后将其作为结果呈现出来。为了测试这个功能,我使用了几行 Python 代码来启动条形码扫描仪,然后用它的二维码指向 SL4A 主页,以下载.apk文件。这是我得到的:

`>>> import android

droid = android.Android() res = droid.scanBarcode() res.result {u'extras': {u'SCAN_RESULT': u'android-scripting.googlecode.com/files/sl4a_…](p6-xtjj-sign.byteimg.com/tos-cn-i-73…) u'SCAN_RESULT_FORMAT': u'QR_CODE'}}`

接下来是search API 函数。您可以使用通用字符串调用此 API 函数,如下所示:

>>> search('pizza')

接下来会发生什么取决于您的设备上有多少不同的应用能够执行搜索。图 5-6 显示了典型安卓手机上的几个选项。你可以选择一个应用并将其设为默认,但它可能不会给你想要的结果。调用pick函数显示基于作为参数传递的统一资源标识符(URI)选择的内容。您可以使用它来显示具有以下代码的联系人列表:

`>>> import android

droid = android.Android() droid.pick('u'content://contacts/people')`

view API 函数基于作为参数传递的 URI 启动一个查看动作。这个函数还接受两个可选参数:type是一个表示 URI MIME 类型的字符串,extras是一个 JSON 对象,包含意图所需的任何额外信息的映射。理解如何使用 API 调用需要了解一些意图和 URIs。基础知识包含在第二章中,尽管重温一下谷歌 Android 开发者网站也无妨。

如果你只是想启动联系人应用,那么使用viewContacts。这个函数使用启动活动调用来简单地启动应用,然后返回给调用者。如果您的设备上碰巧存储了任何 HTML 内容,您可以使用viewHtml功能来显示它。它需要一个完整的文件路径作为单个参数。要在地图上搜索某样东西,请使用带有字符串参数的viewMap函数,该参数包含您要查找的内容。这将启动地图应用,搜索栏包含您的搜索字符串。

images

***图 5-6。*搜索 API 函数的结果

联系

这个外观让您可以访问任何与联系人有关的东西。如果你只是想得到一个包含你设备上所有联系人的大列表,使用contactsGet。您可能希望首先使用contactsGetAttributes呼叫来找出每个联系人的可用信息。随着谷歌改进他们的产品,这个列表可能会随着时间的推移而改变。在检索整个列表之前,您可能想使用的另一个调用是contactsGetCount。这只是返回存储在设备上的联系人总数。

如果您需要选择一个特定的联系人用于其他操作,请使用pickContact功能。这将启动 People 应用,并显示搜索框和键盘。请注意,它返回指向所选联系人的意图。如果你只需要一个联系人的电话号码,你可以使用pickPhone功能。这将像以前一样显示联系人列表,但只在您选择姓名后给出相关的电话号码。该函数返回作为结果一部分的电话号码。这里最后两个函数是contactsGetByIdcontactsGetIds。这两者结合在一起,允许您只使用 ID 来选择特定的联系人。图 5-7 显示了这些功能中的一部分。

images

***图 5-7。*不同联系人 API 函数的结果

SL4A r4 为 contacts facade 引入了一个新的queryContent函数。这个函数总共有五个参数,为了完全定义您希望查询返回的内容,您需要传递它。第一个参数是您希望查询的内容供应器的 URI。对于联系人数据库,这将是content://com.android.contacts/data/phones。其余的参数是可选的,但是如果你想使用默认值,你必须传递关键字'None'。第二个参数是数据库中您希望返回的列的列表。第三个参数是一个选择过滤器,用于选择要从数据库返回的特定行。最后两个参数是selectionArgsorder

下面的一小段代码展示了如何使用这个函数:

import android droid = android.Android() contacts = droid.queryContent('content://com.android.contacts/data/phones',\ ['display_name','data1'],None,None,None).result for c in contacts: print c

您还需要安装至少PythonForAndroid_r6.apk来运行这个例子。也就是说,您应该会看到该代码片段的输出,如下所示:

[{u'data1': u'321-555-1212', u'display_name': u'John Doe'}, {u'data1': u'321-555-1212', u'display_name': u'Jane Doe'}, {u'data1': u'321-555-1212', u'display_name': u'Jed Doe'}, {u'data1': u'321-555-1234', u'display_name': u'John Smith'}, {u'data1': u'321-555-1234', u'display_name': u'Jane Smith'}, {u'data1': u'321-555-1234', u'display_name': u'Jill Smith'}, {u'data1': u'800-555-1212', u'display_name': u'Toll Free'}]

活动能力

Android 操作系统保留了一个事件队列,用于在应用之间异步传递信息。这个外观让您可以访问操作 Android 事件的函数。如果你只是想清除事件缓冲区,只需调用eventClearBuffer。要在队列中添加或删除事件,应该使用eventPosteventPoll。为了等待一个事件,使用带有参数eventNameeventWaitFor,但是要注意这将阻止进一步的执行,直到指定的事件发生。

使用 SL4A API wiki 中的示例代码可以看到一个事件示例。我更新了代码以使用 SL4A r4 函数,如下所示:

import android, time droid = android.Android() droid.startSensingTimed(1,1000) e = droid.eventPoll(1).result event_entry_number = 0 x = e[event_entry_number]['data']['xforce']

函数是实现模态对话框的一种方式,我将在后面的章节中演示。如果您正在构建一个多线程的应用,它也可以作为一个跨线程的通信媒介。

裸眼立面

这个 facade 为 API 3 或更低版本提供 TTS 服务。这里唯一的函数是ttsSpeak,它使用 TTS 功能输出传递的字符串。

定位面

通过 GPS 或使用有关你当前使用的手机信号塔的信息,功能可以让你随时知道你在哪里。如果您使用getLastKnownLocation功能,您将获得的信息可能是最新的,也可能不是。为了确保获得相关信息,您必须调用startLocating调用。随后调用readLocation,您应该会看到如下结果:

Result(id=6, result={u'network': {u'altitude': 0, u'provider': u'network', u'longitude':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) -84.480000000000004, u'time': 1296595452577L, u'latitude': 31.392499999999998, u'speed':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) 0, u'accuracy': 1000}}, error=None)

startLocating将仅使用当前启用的位置资源。这意味着除非您已经启用了 GPS,否则您将无法获得 GPS 定位。使用stopLocating呼叫停止收集位置数据。这个 facade 中最后一个可用的函数是geocode,您可以将它与readLocationgetLastKnownLocation结合使用,以获得给定纬度和经度的地址列表。

将先前的位置输入到geocode会返回以下内容:

Result(id=7, result={u'locality': u'Milford', u'sub_admin_area': u'Baker', u'admin_area':![imagesu'Georgia', u'feature_name': u'Milford', u'country_code': u'US', u'country_name':![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) u'United States'}], error=None)

请记住,此功能需要活跃的互联网连接来进行实际查找。

中型师学院

如果您想播放音乐或视频内容,可以使用这个外观。SL4A r4 使这个函数成为 API 的正式部分。可用的功能提供了打开、关闭、播放、暂停和查找媒体文件中某个位置的方法。功能mediaIsPlayingmediaPlayInfomediaPlayList提供关于媒体播放器当前状态的信息。请记住,这些功能实际上并不操作媒体播放器应用;他们推出了媒体播放器服务。如果您想启动媒体播放器,可以使用startActivity来启动。

函数名很明显:mediaPlaymediaPlayClosemediaPlayPausemediaPlaySeekMediaPlayStart。您将需要使用mediaPlay来实际加载由 URL 指定的媒体资源。如果你正在创建一个类似背景噪声播放器的东西,函数mediaPlaySetLooping是最后一个你可能会觉得方便的函数。

中间商唱片公司

MediaRecorder facade 让您可以访问音频和视频录制功能。您必须为输出文件提供有效的路径,否则调用将失败。如果您只是想启动视频捕捉应用,请使用startInteractiveVideoRecording功能。要开始录音,使用recorderStartMicrophone功能。对于录像,使用recorderCaptureVideo功能。如果您使用其中任何一个,您必须显式调用recorderStop来结束先前开始的记录。

PhoneFacade

每部 Android 手机都可以通过编程实现基本的手机操作。这个外观还包括许多特定于网络的功能。如果设备上没有可用的功能,有些功能会直接返回,没有任何信息。这种行为的一个例子是getCellLocation调用。如果你在 CDMA 手机上打这个电话,结果将是一无所获。如果要监控手机状态,必须先调用startTrackingPhoneState功能。readPhoneState函数返回当前状态以及任何来电的电话号码。

打电话有两种基本方式。首先是phoneCallNumberphoneDialNumber函数。这些函数将字符串形式的电话号码作为唯一的参数。两者的区别在于,phoneCallNumber函数将实际发出呼叫;phoneDialNumber将使用您传递的号码打开电话拨号器,就像在键盘上输入一样。

您也可以使用phoneCallphoneDial功能用 URI 弦拨号。您可以传递由pickContact函数返回的意图,它将调用该联系人的主要号码。要在 Python 中做到这一点,您需要提取由pickContact函数返回的意图。在 Python 中,应该是这样的:

cont = droid.pickContact() droid.phoneDial(cont[1]['data'])

偏好

如果您希望构建一个有自己偏好设置的应用,您将需要这个 facade。SL4A r4 版本支持三个功能:prefGetAllprefGetValueprefPutValue。默认情况下,这三者都在共享首选项存储上运行,其中包含使用情况跟踪首选项。以下是您将从 IDLE 中看到的内容:

`>>> import android

droid = android.Android() pref = droid.prefGetAll() pref Result(id=0, result={u'usagetracking': False, u'present_usagetracking': False}, error=None)`

要创建自己的首选项文件,您需要添加一个文件名作为参数传递给GetPut例程,如下所示:

`>>> droid.prefPutValue('GPSTracking', True, 'myprefs') Result(id=7, result=None, error=None)

droid.prefGetValue('GPSTracking','myprefs') Result(id=9, result=True, error=None)`

传感器管理人员的能力

每个 Android 设备都有一个或多个传感器可供应用使用。至少,有一个加速度计来确定屏幕的方向。SensorManager facade 提供了对 Android 当前支持的所有传感器的访问。这也是另一种需要你开始和停止感知过程的门面类型,因为这发生在后台。要开始和停止感应,使用startSensingstopSensing功能调用。一旦您开始感测并等待一段时间以允许收集传感器数据,数据将可用。

最高层是readSensors函数调用。以下示例显示了此函数返回的数据:

`>>> res = droid.readSensors()

import pprint pprint.pprint(res.result) {u'accuracy': 3, u'azimuth': -2.734636402130127, u'pitch': -1.0204463958740235, u'roll': 0.034272377938032152, u'time': 1296683466.802, u'xforce': -0.14982382999999999, u'xmag': 13.75, u'yforce': 8.6625409999999992, u'ymag': -38.4375, u'zforce': 5.3664170000000002, u'zmag': 15.375}`

有单独的函数调用返回特定的信息:sensorsGetAccuracysensorsGetLightsensorsReadAccelerometersensorsReadMagnetometersensorsReadOrientation。调用这些函数的结果如下所示:

`>>> droid.sensorsGetAccuracy() Result(id=7, result=3, error=None)

droid.sensorsGetLight() Result(id=8, result=None, error=None) droid.sensorsReadAccelerometer() Result(id=9, result=[-0.14982382999999999, 8.7306430000000006, 5.4345189999999999],images error=None) droid.sensorsReadMagnetometer() Result(id=10, result=[11.25, -37.6875, 13.3125], error=None) droid.sensorsReadOrientation() Result(id=11, result=[-2.7596172332763671, -1.0129913330078124, 0.035179258137941358], images error=None)`

加速度计和磁力计函数返回 X、Y 和 z 值列表。方向返回方位角、俯仰角和滚动角列表。在 SL4A r4 中,startSensing功能已被弃用,代之以startSensingThresholdstartSensingTimed。在许多情况下,当您需要使用传感器时,您要么希望根据时间来检测运动,要么希望设备跨越某个运动阈值。startSensingThreshold功能允许您在方位、运动(加速度计)、方向(磁力计)或光线超过特定阈值时,将传感器事件记录到事件队列中。如果您希望使用多个传感器,您必须多次调用startSensingThreshold来启用每个传感器的特定阈值。startSensingTimed函数采用两个参数来确定要记录哪个传感器(1 = all,2 =加速度计,3 =磁力计,4 =光),以及一个delayTime(以毫秒为单位)参数来指定读数之间的时间间隔。

集合完毕

这种外观让您可以访问手机上的所有不同设置:铃声音量、屏幕亮度等。当你考虑为你的设备编写脚本时,这可能是更有用的外观之一。后面的章节将使用这些函数调用来演示 SL4A 的强大功能。现在,让我们来看看有什么可用的。

有三个函数调用简单地检查某个东西的状态。这些包括checkAirplaneModecheckRingerSilentModecheckScreenOn。这三个函数都返回一个布尔值,表明模式是开(True)还是关(False)。要知道checkScreenOn至少要求 API 等级 7 以上(安卓 2.1)。要更改AirplaneModeRingerSilentMode,您可以使用toggleAirplaneModetoggleRingerSilentMode。这些函数类似于其他切换函数,因为您可以通过传递可选参数来显式设置模式。返回的结果将反映设备的当前状态。还有一个toggleVibrateMode来设置设备只有在铃声启用时才振动,否则在收到新通知时振动。

其余的函数要么获取特定的设置,要么为某个设置设置一个值。为了得到一个值,你使用getMaxMediaVolumegetMaxRingerVolumegetMediaVolumegetRingerVolumegetScreenBrightnessgetScreenTimeoutgetVibrateMode。要设置数值,应该使用setMediaVolumesetRingerVolumesetScreenBrightnesssetScreenTimeout

信号强度外观

如果你想知道或显示你的信号有多好,你应该使用这个门面。首先,您必须调用startTrackingSignalStrengths函数来开始收集数据。接下来,您应该调用 readSignalStrengths 来实际读取数据。它将返回如下内容:

>>> droid.readSignalStrengths().result {u'cdma_ecio': -70, u'evdo_dbm': -98, u'cdma_dbm': -97, u'evdo_ecio': -1515,![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) u'gsm_signal_strength': 99, u'gsm_bit_error_rate': -1}

一旦你完成了,你应该发出stopTrackingSignalStrengths来关闭这个进程。

smsf acad

这个外观允许您操作手机上存储的 SMS 消息。它有许多删除、阅读、标记和发送短信的功能。短信是谷歌决定让属性更灵活的另一个领域。smsGetAttributes函数返回当前定义的可用属性列表。使用 Python 和pprint函数将显示以下内容:

>>> pprint.pprint(droid.smsGetAttributes().result) [u'_id', u'thread_id', u'toa', u'address', u'person', u'date', u'protocol', u'read', u'status', u'type', u'reply_path_present', u'subject', u'body', u'sc_toa', u'report_date', u'service_center', u'locked', u'index_on_sim', u'callback_number', u'priority', u'htc_category', u'cs_timestamp', u'cs_id', u'cs_synced', u'error_code', u'seen']

如果您想知道设备上当前存储了多少条短信,请使用smsGetMessageCount。该函数有一个必需的布尔参数,用于指示您是希望统计未读邮件还是所有邮件。如果你没有给它传递一个参数,你会得到这样一个错误信息:

>>> droid.smsGetMessageCount() com.googlecode.android_scripting.rpc.RpcError: Argument 1 is not present Result(id=24, result=None, error=u'com.googlecode.android_scripting.rpc.RpcError: Argument 1![images](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/897e5821fa0e444da5a20fd6f40f63bf~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1776093973&x-signature=SBxnNbmNzTjM7CmFiUXOzfZkOb4%3D) is not present')

TrueFalse作为参数调用它将返回一个整数计数,如下所示:

`>>> droid.smsGetMessageCount(True).result 0

droid.smsGetMessageCount(False).result 228`

操纵单个消息是通过 ID 完成的。调用smsGetMessageIds将返回所有消息 id 的列表或未读消息,这取决于传递的布尔参数。这只是返回一个数字列表,所以如果你真的想用它们做什么,你必须得到所有的消息。有两种方法可以做到这一点。要么调用smsGetMessages获取所有信息,要么遍历由smsGetMessageIds返回的消息 id 列表,然后使用smsGetMessageById分别获取每个消息 id。

如果您只想处理未读邮件,您可以设置传递给任何 GetMessage 调用的布尔值。然后,您可以使用smsMarkMessageReadsmsDeleteMessage来处理每条消息。最后,还有真正发送短信的smsSend。这个函数有两个参数:目的地址(通常是电话号码)和消息的实际文本。

演讲认知面

您可以使用这个外观将语音识别添加到您的脚本中。它只有一个名为recognizeSpeech的函数调用。有三个可选参数,包括提示字符串、通知识别器预期使用不同于默认语言的语音的语言字符串,以及告诉识别器首选哪个语音模型的语言模型字符串。它返回一个字符串,表示尽最大努力将语音转换为文本。如果它不能解释它,你将得到一个空字符串。下面是我调用这个函数,说“西班牙的雨主要落在平原上”时得到的结果:

>>> droid.recognizeSpeech() Result(id=2, result=u'the rain in spain falls mainly on the plane', error=None)

TextToSpeechFacade

这些函数为 API4 和更高版本提供 TTS 服务。要让设备“说出”一个短语,您可以使用ttsSpeak函数,向它传递一个包含该短语的字符串。发出此调用后,控制权会立即传递回调用脚本。您必须使用功能ttsIsSpeaking来确定语音功能是否已经完成。

音发生器面

如果您需要为特定功能(如与交互式语音应答应用交互)生成 DTMF 音调,这款电话正适合您。要使用它,您必须调用generateDtmfTones,传入一个表示您希望生成的数字的字符串。可选的整数参数允许您改变每个音的持续时间,默认值为 100 毫秒。

简易性

这个 facade 提供了创建用户界面元素所需的所有函数,比如文本框、复选框、日期选择器等等。其中一些函数是单动作,这意味着你只需要调用它们一次就能得到响应。这些功能还会阻止或等待用户完成操作并关闭对话框。两个单动作、与输入相关的对话框是dialogGetInputdialogGetPassword。两者都有可选参数来设置标题、提示消息和默认输入。图 5-8 显示了以下代码的结果:

>>> droid.dialogGetInput(u'My Title', u'My Message')images

***图 5-8。*对话输入示例

这个外观中有许多函数需要两次调用来实际显示对话框,第三次调用来获得响应。这个过程包括用一个调用来设置对话框,然后用一个对dialogShow的调用来呈现它。图 5-9 显示了dialogCreateAlert的一个例子。

images

***图 5-9。*对话示例创建警报

这个特殊的对话框旨在向用户呈现某种类型的警告信息,这需要在设备上执行任何操作之前进行确认。它不会返回任何信息,也不会阻止任何进一步的程序执行。您可以通过按下一个硬件按钮或调用dialogDismiss以编程方式关闭对话框。

对于其他要返回信息的 UI 元素,您需要调用dialogGetResponse来实际获取数据。这里的顺序很重要,因为dialogGetResponse实际上会阻塞,直到用户关闭对话框。您应该检查结果变量,以确定用户是实际输入了数据还是按下了 Cancel 按钮。要提示并实际获得与dialogCreateTimePicker,的时间,请执行以下操作:

`>>> droid.dialogCreateTimePicker() Result(id=22, result=None, error=None)

droid.dialogShow() Result(id=23, result=None, error=None) droid.dialogGetResponse() Result(id=24, result={u'hour': 15, u'minute': 53, u'which': u'positive'}, error=None)`

您可以在使用 Python 和 IDLE 时看到调用dialogGetResponse的结果,因为提示会消失,直到您关闭对话框。如果用户单击 Cancel 按钮,您将在'which'参数中得到一个正的返回,如下所示:

>>> droid.dialogGetResponse() Result(id=26, result={u'hour': 0, u'minute': 0, u'which': u'negative'}, error=None)

有三个函数调用允许您设置警告框中显示的按钮文本。这里有一个简短的 Python 例程来演示dialogSetPositiveButtonTextdialogSetNegativeButtonTextdialogSetNeutralButtonText的用法。图 5-10 显示了实际的对话框。

`import android

droid = android.Android()

title = 'Alert' message = ('This alert box has 3 buttons ' 'and waits for you to press one.') droid.dialogCreateAlert(title, message) droid.dialogSetPositiveButtonText('Yes') droid.dialogSetNegativeButtonText('No') droid.dialogSetNeutralButtonText('Cancel') droid.dialogShow() response = droid.dialogGetResponse().result

print ['which'] in ('positive', 'negative', 'neutral')`images

***图 5-10。*多按钮警告对话框示例

下一组对话框功能包含多个元素,必须在显示它们之前进行设置。这些元素包括以单项或多项选择方式进行选择的项目列表。图 5-11 显示了使用dialogSetItems创建列表。下面是设置项目列表的一小段 Python 代码:

droid.dialogCreateAlert(title) droid.dialogSetItems(['one', 'two', 'three']) droid.dialogShow() response = droid.dialogGetResponse().resultimages

***图 5-11。*多选项警告对话框示例

这个主题的一个小变化是使用dialogSetSingleChoiceItemsdialogSetMultiChoiceItems来创建一个项目列表,用单选按钮或复选框来选择项目。使用dialogSetItemsdialogSetSingleChoiceItems的唯一真正区别是带有单选按钮和确认选择和返回按钮的可视显示。下面是使用dialogSetSingleChoiceItems的代码:

droid.dialogCreateAlert(title) droid.dialogSetSingleChoiceItems(['One', 'Two', 'Three']) droid.dialogSetPositiveButtonText('Done') droid.dialogShow()

对于多选选项,还需要一次函数调用来获取选中的项目:dialogGetSelectedItems。在这个例子中,操作的顺序也很重要。您必须等待用户实际选择项目并关闭对话框,然后才能尝试读取它们。因此,您必须在调用dialogGetResponse之后插入对dialogGetSelectedItems的调用。图 5-12 显示了对话框的样子。下面的代码片段展示了如何使用这个调用来创建对话框并获得响应:

droid.dialogCreateAlert(title) droid.dialogSetMultiChoiceItems(['One', 'Two', 'Three']) droid.dialogSetPositiveButtonText('Done') droid.dialogShow() droid.dialogGetResponse() ans = droid.dialogGetSelectedItems()images

***图 5-12。*多选警告对话框示例

选择选项一和三的结果如下:

Result(id=5, result=[0, 2], error=None)

在 Python 中,结果实际上是代表所选选项的值列表(从零开始)。如果您有任何需要一些时间才能完成的脚本代码,您应该使用进度对话框来通知用户。可用的两个选项是dialogCreateHorizontalProgressdialogCreateSpinnerProgress。要更新进度设置,您必须呼叫dialogSetCurrentProgress。还有dialogSetMaxProgress来定义终点。图 5-13 显示了用以下代码生成的水平进度条:

`import android import time

droid = android.Android()

title = 'Horizontal' message = 'This is simple horizontal progress.' droid.dialogCreateHorizontalProgress(title, message, 100) droid.dialogShow() for x in range(0, 99): time.sleep(0.1) droid.dialogSetCurrentProgress(x) droid.dialogDismiss()`images

***图 5-13。*水平进度对话框示例

请注意,您必须调用dialogDismiss才能真正让进度对话框消失。微调器进度对话框是用dialogCreateSpinnerProgress创建的,用于显示移动的东西,让用户知道正在进行加工。和水平进度对话框一样,你必须调用dialogDismiss来关闭微调对话框。

Android 为应用提供了两种类型的菜单:上下文和选项。一个上下文菜单类似于你在桌面操作系统中右击鼠标时看到的内容。选项菜单是您在脚本运行时按下设备菜单按钮时看到的内容。然后,用户可以设置偏好,甚至以相对标准的 Android 方式退出脚本。您可以使用addContextMenuItemaddOptionsMenuItem向这些菜单添加项目。要清除任一菜单,使用clearContextMenuclearOptionsMenu

最后的 UI 对话框元素是webViewShow。这个对话框向 SL4A 脚本打开了 HTML 表单的世界,并将在后面的章节中用来构建一个全功能的应用。现在,让我们假设它将使用传递给它的 URL 显示一个 WebView。如果设置为True,可选的wait布尔参数将导致脚本阻塞,直到用户退出 WebView。第八章使用这个外观来构建一些基于对话框的用户界面例子。

醒脑脸

在移动设备应用领域,有一个概念是将设备锁定在唤醒状态,以允许一些关键过程完成。这对你的电池寿命有潜在的危险,应该只在短时间内使用。它也可以用于像视频播放器这样的应用,以防止正常的屏幕关闭。为创建唤醒锁而提供的函数调用包括wakeLockAcquireBrightwakeLockAcquireDimwakeLockAcquireFullwakeLockAcquirePartial。每一个都影响屏幕亮度和 CPU 状态。当你的应用不再需要唤醒锁时,它调用wakeLockRelease函数来关闭它。

网络广播学院

Android 设备上的网络摄像头是前置摄像头。使用webcamStartwebcamStop来启动或停止网络摄像头。当您启动网络摄像头时,您可以使用分辨率、质量和端口号的默认设置,或者将它们作为选项传入。还有一个单独的功能,webcamAdjustQuality,用于在播放视频时调整质量。

wif academy

通过WifiFacade,你可以完全控制你设备上的 WiFi 收音机。基本操作是checkWifiStatetoggleWifiState。这些函数的操作方式与其他类似命名的函数非常相似,这意味着您可以向toggleWifiState传递一个布尔值来隐式启用或禁用 WiFi 无线电。对wifiDisconnectwifiReconnectwifiReassociate的调用顾名思义。要检索当前活动接入点的信息,使用wifiGetConnectionInfo

您可以使用剩余的函数调用来构建一个 WiFi 扫描应用。可用的函数调用有wifiStartScanwifiGetScanResultswifiLockAcquireFullwifiLockAcquireScanOnlywifiLockRelease。如果您想独享 WiFi 广播,您应该拨打wifiLockAcquireFullwifiLockAcquireScanOnly。确保完成后调用wifiLockRelease,否则其他应用将无法连接到 WiFi。

总结

本章的重点是让你熟悉 SL4A 提供的 Android APIs。例子给出了所有使用的 Python 和运行在 Windows 上的空闲应用。通过使用相同的基本方法,您应该能够在 Linux 或 Mac OS X 上重复这些示例。在下一章中,我将开始实际创建您可以立即投入使用的真实脚本。

以下是你想从这一章中记住的一些事情。

  • 物是人非:SL4A 项目是一个动态的项目,新的版本经常会给 API 带来变化。如果某个特定的功能被替换了,你会收到一个通知。
  • 了解你的门面 : SL4A 使用门面的概念来模仿原生 Android API 调用。这将有助于您了解本地调用是如何工作的,尤其是对于像startActivitymakeIntent这样的东西。
  • 不要害怕尝试:模拟器是测试许多 API 调用的好地方。不幸的是,并非所有的功能都可以在模拟器中工作。传感器、摄像头、WiFi 和网络摄像头只能在真实设备上工作。在一台设备上测试这些功能不会对你造成任何伤害。所以去尝试一下吧。
  • 阅读文档:我知道有时候阅读文档有多难。在使用 Android 和 SL4A 的情况下,如果你只是做一点阅读,它可以节省你的时间和挫折。谷歌搜索也可以成为你的朋友。