要运行本文的 示例代码,应该安装 Android SDK 1.5 或更晚版本和 Eclipse。要进一步学习如何设置环境,请访问 Android Developers 网站。参见 参考资料 中的链接。
简介
难以想象还有哪种技术比移动电话更流行。大量的平台在销售和心理份额方面争占此行业的顶级市场。设备是高级的工程样品,真正让它们流行起来的动力在于这些平台上可用的大量应用程序带来的用户体验。具体来说,iPhone 和 Android 平台是最新的设备,点燃了人们的消费欲望。
对移动用户可用的大部分应用程序是由移动开发人员通过各平台供应商提供的核心 SDK 编写的。移动设备的流行离不开大量天才的 web 技术程序员,过去十年间,他们在 Web 上大获成功,现在又开创了一种新型的应用程序 — 混合应用程序,即既使用 web 浏览器接口又使用本地移动组件的应用程序。针对 iPhone 和 Android 都存在混合应用程序,不过本文的重点放在 Android 混合应用程序及 JavaScript 和 JSON 的使用上。
混合应用程序是用 Android 的 WebView 控件中的 WebKit 引擎构建的。这是一个用户界面小部件,它向 Android 程序员提供 WebKit 的功能。此控件可用于在应用程序中呈现远程 web 页面,以便为开发人员和用户等提供熟悉的用户界面体验,以及在本地 Android 应用程序中利用强大且灵活的 JavaScript 环境。
混合应用程序通常利用 WebView 小部件来为用户界面元素充分利用 WebKit 的优势,但是混合应用程序不仅仅是简单地在小部件中显示一些 HTML。混合应用程序是多才多艺的 — Android SDK 中包含的广泛功能加上 HTML、CSS 和 JavaScript 等 web 技术,使得混合应用程序的功能不可限量。为了实际了解混合应用程序的概念,本文将介绍一个名为 AndroidJSON 的样例应用程序,它实现了 Activity、WebView 和 JSON 之间的很多交互,以交换数据。该应用程序演示了 Activity 和 WebView 宿主的 HTML 及 JavaScript 之间的很多交互,主要特性是一个 JavaScript 计算器。
首先,我们来看直接在 Android 应用程序中嵌入一个 WebKit 引擎。
回页首
嵌入在 Android 中的 JavaScript 计算器
大部分基于 SDK 的 Android 应用程序都包含 Activity
类的一个或多个实现。Activity
类本质上是一个屏幕或页面,其中包含由应用程序用户体验的用户界面元素。
Activity
显示一组由程序员定义的用户界面元素,比如按钮、标签、文本输入框、单选按钮列表,等等。所有预期的条目都可在 Android SDK 中找到。除了这些用户界面元素之外,还有一个特殊的小部件,就是 WebView。
JavaScript 计算器演示了 Activity 的 Java 环境和 WebView 的 JavaScript 环境之间相辅相成的关系。应用程序不仅仅是要求 WebView 显示 HTML 内容 — 它实际上是连接 Java 环境,以向 JavaScript 环境提供功能,这样可以将两者紧密地集成在一起,从而带来独特的用户体验。一旦两个环境连接起来,就可以 JSON 形式交换数据,以交付各种特性,本文将全面解释这些特性。我们首先来看 JavaScript 计算器如何利用 WebView 小部件。
在深入应用程序是如何构造的细节之前,先花点时间回顾一下应用程序的各种特性。图 1 展示了应用程序屏幕。
图 1. 展示正在工作的 JavaScript 计算器
在名为 AndroidJSON 的样例本地 Android 应用程序中,屏幕是用 Activity
组件定义的。它在屏幕的上半部分包含传统的用户界面元素,比如一个 TextView
(静态标签)、一个 EditText
(文本框,用户在这里输入公式)和三个按钮(即 Simple、Complex 和 Make Red)。Activity
也具有 WebView
控件的单个实例,用于显示屏幕的下半部分。
WebView 显示一个与 Android 应用程序打包在一起的 HTML 文件 (index.html),不过您也可以从 Internet 单独下载此文件。该 web 页面包含标题、一些样例文本、计算结果和六个执行各种功能的按钮(Log Info、Log Error、Dynamic、How Many Calls、History 和 Kill This App)。
这个项目中最有趣的文件是 AndroidJSON.java(Android 应用程序代码)、index.html(web 页面)和 main.xml(一个 UI 布局文件,后面将会介绍)。参见 下载 部分到这些文件的链接。
首先,来看 Activity
中三个按钮的功能:
- Simple
- Simple 按钮导致 EditText 的内容被作为数学表达式进行计算。注意,EditText 的内容或者说公式,在 JavaScript 中被传递到 WebView 控件并进行计算。
- Complex
- Complex 按钮将 JSON 对象发送到 WebView 进行计算。这被认为复杂,是因为对象随后在 JavaScript 代码中被解释并以数学方式被操纵。该按钮在两个功能之间交替,一个功能是将一个整数数组的元素相加,另一个功能是将这个整个数组的元素相乘。
- Make Red
-
这第三个按钮在此主要是出于好玩。选中时,该按钮向嵌入的 WebView 内容应用一种样式,将包含在
<body>
标记中的文本元素变成红色。
现在来看 index.html 文件中的函数,该文件由嵌入的 WebView 控件在运行时启用。
- Log Info
- 该按钮调用 Android 应用程序中的一个回调函数,以将数据项写到 Info 分类下的应用程序日志中。
- Error Info
- 该按钮调用 Android 应用程序中的一个回调函数,以将数据项写到 Error 分类下的应用程序日志中。
- Dynamic
-
该按钮调用 Android 应用程序中的一个回调函数,以检索一段代表有效 JavaScript 代码的文本。此代码被带回 WebView 中并执行,演示了应用程序两端之间的交互。注意,此方法存在安全隐患,因为它盲目信任 JavaScript
eval
函数。但是,我们这里将重点放在基本的示例应用程序上,而不是介绍完善的生产性应用程序。 - How many calls
- 每调用一次回调函数,计数器就会增 1。 该按钮只是显示计数器。
- History
- 每调用一次 JavaScript 函数,一个表示函数名的字符串就会被添加到 JavaScript 数组。当 history 按钮被调用时,此数组将被转换成 JSON 并传递到 Android 应用程序的本地部分。数组被改造为 Java 代码中的一个对象,并枚举写到日志中的每个数组元素。
- Kill This App
-
该按钮是此应用程序的又一个只是出于好玩的特性。该按钮调用一个会通过调用
finish()
而终止 Android 活动的回调函数。
跟很多不完善的应用程序一样,此 Android 应用程序也使用了内置在 Android 中的日志功能。本文中展示的一些屏幕截图来自 Eclipse 中的 Dalvik Debug Monitor Service (DDMS) 视图,其中 LogCat 窗口是可见的。要获得更多关于如何使用 Android 开发工具的信息,请参考 参考资料 中的链接。
刚才解释了应用程序的函数,现在来看用户界面是如何构造的。
回页首
设置用户界面
为该应用程序创建用户界面要调用前面介绍过的三个文件。首先是布局文件 main.xml,如 清单 1 所示。
清单 1. main.xml,用户界面布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/title" /> <EditText android:id="@+id/formula" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" android:visible="False" /> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:text="Simple" android:id="@+id/btnSimple" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> <Button android:text="Complex" android:id="@+id/btnComplex" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> <Button android:text="Make Red" android:id="@+id/btnRed" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> </LinearLayout> <WebView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/calculator" android:layout_weight="1" /> </LinearLayout> |
在 清单 1 中,布局包含各种用户界面元素。注意,android:id
属性使得应用程序可以引用布局中的特定小部件。例如,WebView 包含 calculator
的一个 id
;但是 TextView
不包含 id,因为它的值在应用程序的整个生命期内是不变的。
AndroidJSON.java 中的 onCreate()
方法负责搭建布局,如 清单 2 所示。
清单 2. 设置用户界面
public class AndroidJSON extends Activity { private final String tag = "AndroidJSON"; private WebView browser = null; private int flipflop = 0; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final EditText formula = (EditText) this.findViewById(R.id.formula); final Button btnSimple = (Button) this.findViewById(R.id.btnSimple); final Button btnComplex = (Button) this.findViewById(R.id.btnComplex); final Button btnRed = (Button) this.findViewById(R.id.btnRed); // remaining code removed for brevity - shown in next listings } |
通过调用 setContentView()
搭建布局。注意,通过调用 findViewById()
方法设置用户界面元素。每次保存 main.xml 文件时会自动产生 R.java 文件。包含 android:id
属性的布局元素变成 R.id
类中的值,如 清单 3 所示。
清单 3. R.java
/* AUTO-GENERATED FILE. DO NOT MODIFY. * * This class was automatically generated by the * aapt tool from the resource data it found. It * should not be modified by hand. */ package com.msi.androidjson; public final class R { public static final class attr { } public static final class drawable { public static final int icon=0x7f020000; } public static final class id { public static final int btnComplex=0x7f050002; public static final int btnRed=0x7f050003; public static final int btnSimple=0x7f050001; public static final int calculator=0x7f050004; public static final int formula=0x7f050000; } public static final class layout { public static final int main=0x7f030000; } public static final class string { public static final int app_name=0x7f040001; public static final int title=0x7f040000; } } |
本文稍后还会详细介绍 Button 设置代码,现在将重点放在 WebView 控件或小部件的设置上。尽管 Button 和其他用户界面元素相当直观,但是 WebView 还是得稍微费点功夫。但是不必担心 — 它也不是那么难,具体来说重点就是使用常用的剪切粘贴技术!来看一下 清单 4 中的代码段,它还是来自 AndroidJSON.java 中的 onCreate()
方法。
清单 4. 设置 WebView 小部件
// connect to our browser so we can manipulate it browser = (WebView) findViewById(R.id.calculator); // set a webview client to override the default functionality browser.setWebViewClient(new wvClient()); // get settings so we can config our WebView instance WebSettings settings = browser.getSettings(); // JavaScript? Of course! settings.setJavaScriptEnabled(true); // clear cache browser.clearCache(true); // this is necessary for "alert()" to work browser.setWebChromeClient(new WebChromeClient()); // add our custom functionality to the javascript environment browser.addJavascriptInterface(new CalculatorHandler(), "calc"); // uncomment this if you want to use the webview as an invisible calculator! //browser.setVisibility(View.INVISIBLE); // load a page to get things started browser.loadUrl("file:///android_asset/index.html"); // allows the control to receive focus // on some versions of Android the webview doesn't handle input focus properly // this seems to make things work with Android 2.1, but not 2.2 // browser.requestFocusFromTouch(); |
注意,在 清单 4 中,您将一个名为 browser
的 Activity 范围变量捆绑到了 WebView 控件。WebView
是一个相当复杂的类,可高度定制。例如,您需要设置几个类,以得到与 web 浏览器相关的预期函数。这是程序员必须投入一定精力来得到一些有用函数的地方之一。但是,此定制是没有限制的。对于此应用程序的目的来说,WebView 控件已经进行了最低限度的部署。
WebViewClient
提供用于捕获各种事件的钩子,这些事件包括页面加载开始和结束、表单重新提交、键盘截取以及程序员喜欢跟踪并操纵的很多其他事件。类似地,您需要 WebChromeClient
的一个实例,用于允许诸如非常有用的 alert()
JavaScript 函数之类的函数。使用 WebSettings
来为控件启用 JavaScript。
要导致 WebView 控件导航到一个页面,可以采用几种不同的方式。在这个应用程序中,您采用 loadurl()
方法,带有到打包为项目资产的 index.html 文件的全限定路径。要获得更多关于设置 WebView 控件的信息,请在线查看 android.webkit
包的文档(参见 参考资料)。名为 index.html 的文件直接从应用程序随带的资源加载到 Webview 控件中。注意 图 2 中资源下面的 assets 文件夹。此文件夹是存储混合应用程序中使用的 html 文件的理想位置。(查看 图 2 的文本版本。)
图 2. Eclipse 中的项目
处理 WebView 最重要且有趣的方面是下一步:将 WebView 的 JavaScript 环境连接到 Android Activity 代码。
回页首
连接 JavaScript 接口
下一步是启用 Activity 中的 Java 代码,以与 WebView 管理的 HTML 文件中的 JavaScript 代码交互。这是通过调用 addJavascriptInterface()
方法完成的,如 清单 4 所示。
该函数的参数是一个 Java 类的实例和一个名称空间标识符。例如,对于这个应用程序,您定义一个 calc
名称空间,并实现名为 CalculatorHandler
的类中的代码,如 清单 5 所示。
清单 5.
CalculatorHandler
实现
// Javascript handler final class CalculatorHandler { private int iterations = 0; // write to LogCat (Info) public void Info(String str) { iterations++; Log.i("Calc",str); } // write to LogCat (Error) public void Error(String str) { iterations++; Log.e("Calc",str); } // sample to retrieve a custom - written function with the details provided // by the Android native application code public String GetSomeFunction() { iterations++; return "var q = 6;function dynamicFunc(v) { return v + q; }"; } // Kill the app public void EndApp() { iterations++; finish(); } public void setAnswer(String a) { iterations++; Log.i(tag,"Answer [" + a + "]"); } public int getIterations() { return iterations; } public void SendHistory(String s) { Log.i("Calc","SendHistory" + s); try { JSONArray ja = new JSONArray(s); for (int i=0;i<ja.length();i++) { Log.i("Calc","History entry #" + (i+1) + " is [" + ja.getString(i) + "]"); } } catch (Exception ee) { Log.e("Calc",ee.getMessage()); } } } |
在 JavaScript 环境中,通过 window.calc.methodname
语法访问 CalculatorHandler
的方法。例如,CalculatorHandler
实现一个名为 Info()
的方法,后者接受一个字符串参数并将之写到应用程序日志中。要从 JavaScript 环境访问此方法,可使用类似这样的语法:window.calc.Info("write this string to the application log!");
。
基本了解了如何从 JavaScript 代码调用 Java 代码之后,我们再来看 清单 6 中的 index.html 文件,看各种方法是如何被调用的。
清单 6. WebView 控件中呈现(和执行)的 index.html
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=0.25, user-scalable=yes" /> <title>Android to JavaScript with JSON</title> </head> <script language="JavaScript"> var cmdHistory = new Array(); function startup() { try { window.calc.Info("Starting up...."); cmdHistory[cmdHistory.length] = "startup"; } catch (ee) { } } function PerformSimpleCalculation(formula) { try { cmdHistory[cmdHistory.length] = "PerformSimpleCalculation"; var answer = eval(String(formula)); document.getElementById('data').value = answer; window.calc.setAnswer(answer); } catch (ee) { window.calc.Error(ee); } } function PerformComplexCalculation(andmethod) { try { /* * argument to this function is a single object with 2 "members or properties" * operation: this is a string naming what we want the function to do. * array of arguments: this is an array of integers * */ //alert(andmethod.operation); //alert(andmethod.arguments.length); if (andmethod.operation == "addarray") { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-addarray"; var i; var result = 0; for (i=0;i<andmethod.arguments.length;i++) { result += andmethod.arguments[i]; } document.getElementById('data').value = result; window.calc.setAnswer(result); } if (andmethod.operation == "multarray") { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-multarray"; var i; var result = 1; for (i=0;i<andmethod.arguments.length;i++) { result *= andmethod.arguments[i]; } document.getElementById('data').value = result; window.calc.setAnswer(result); } } catch (ee) { window.calc.Error(ee); } } function dynamicfunction() { try { cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic"; eval(String(window.calc.GetSomeFunction())); var result = dynamicFunc(parseInt(document.getElementById('data').value)); document.getElementById('data').value = result; }catch (ee) { alert(ee); } } </script> <body > <center> <h3>Running in Web View :)</h3> this is some sample text here <br /> <input type="text" id="data" value="starting value"><br /> <button onclick="window.calc.Info(document.getElementById('data').value);">Log Info</button> <button onclick="window.calc.Error(document.getElementById('data').value);">Log Error</button><br /> <button onclick="dynamicfunction();">Dynamic</button> <button onclick="alert(String(window.calc.getIterations()));">How Many Calls</button> <button onclick="window.calc.SendHistory(JSON.stringify(cmdHistory));"> History</button> <button onclick="if (window.confirm('End App?')) window.calc.EndApp();">Kill This App</button><br /> </center> </body> </html> |
仔细研究一下 清单 6 末尾的按钮处理程序。基本上,这些按钮处理程序都调用 window.calc
名称空间中的方法,这些方法在 AndroidJSON.java 中的 CalculatorHandler
类中实现。
清单 5 和 清单 6 协同工作,演示了 JavaScript 环境中初始化的和 Java 源文件中实现的代码交互。但是如何从 Activity 代码中初始化一些您想要在 WebView 中发生的动作呢?
现在应该更深入地来看 Java 代码了。
回页首
插入 JavaScript 代码
从将一个数学公式传递到 JavaScript 代码进行计算这样一个任务开始。JavaScript 最伟大(也最危险)的特性之一是 eval()
函数。eval()
函数允许字符串代码的运行时计算。在本例中,您从 EditText 控件接受一个字符串并传递到 JavaScript 环境进行计算。具体来说,我们调用 清单 6 中的 PerformSimpleCalculation()
函数。
清单 7 包含 AndroidJSON.java 中的代码,它负责处理按钮选择。
清单 7. 从 Java 调用
PerformSimpleCalculation()
JavaScript 函数
btnSimple.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.i(tag,"onClick Simple"); // Perform action on click try { String formulaText = formula.getText().toString(); Log.i(tag,"Formula is [" + formulaText + "]" ); browser.loadUrl("javascript:PerformSimpleCalculation(" + formulaText + ");"); } catch (Exception e) { Log.e(tag,"Error ..." + e.getMessage()); } } }); |
不管此方法有多少行,这里唯一要关注的是 browser.loadurl()
行,它传递一个格式字符串:javascript:<code to execute>
。
此 JavaScript 代码被注入到 WebView 的当前页面并执行。这样,Java 代码就可以执行 WebView 中定义的 JavaScript 代码了。
在 Simple 例子中,传递了一个字符串。但是,当需要处理更复杂的结构时该怎么办呢?这就是 JSON 可派上用场的地方。清单 8 展示了 PerformComplexCalculation()
函数的调用,该函数参见 清单 6。
清单 8. 通过传递一个 JSON 对象调用更复杂的函数
btnComplex.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.i(tag,"onClick Complex"); // Perform action on click try { String jsonText = ""; if (flipflop == 0) { jsonText = "{ \"operation\" : \"addarray\",\"arguments\" : [1,2,3,4,5,6,7,8,9,10]}"; flipflop = 1; } else { jsonText = "{ \"operation\" : \"multarray\",\"arguments\" : [1,2,3,4,5,6,7,8,9,10]}"; flipflop = 0; } Log.i(tag,"jsonText is [" + jsonText + "]" ); browser.loadUrl("javascript:PerformComplexCalculation(" + jsonText + ");"); } catch (Exception e) { Log.e(tag,"Error ..." + e.getMessage()); } } }); |
研究一下 清单 6 中的 JavaScript 函数 PerformComplexCalculation
。注意,传递进来的参数不是字符串,而是您自己创建的一个对象。
operation
- 要处理的函数或过程的名称arguments
- 这是一个整数数组
对象只包含两个属性,但是完全可以更复杂,以满足更高的需求。在本例中,PerformComplexCalculation()
JavaScript 函数支持两种不同的操作:addarray 和 multarray。当这些操作在调用时完成其工作时,通过调用函数 window.calc.setAnswer
,将结果传递回 Java 代码。这里,您看到了 Java 和 JavaScript 代码之间的双向数据流。
在本例中,您传递了一个 JSON 对象,但是得到的一条经验是,在处理从 Java 代码返回来的 Java 字符串时,它有助于将它们转换成 JavaScript 字符串。这可以像本例中一样通过将值传递给 String 函数来做到:eval(String(formula));
。
JavaScript eval()
函数使用 JavaScript 字符串。无需转换的话,eval
函数基本上不做任何事情。
对于一个稍微复杂一点的例子,鼓励您好好看一下 Dynamic 按钮在 WebView 中被选中时的代码段。
要完成代码例子,来看一下将一个字符串数组从 JavaScript 环境传递到 Java 环境。
回页首
交换 JSON 对象
示例应用程序 (index.html) 中的 JavaScript 代码将本地函数调用记录到一个名为 cmdHistory
的页面级别数组中。每次调用函数时,您都将一个新条目添加到该数组中。例如,当 dynamicfunction()
被调用时,一个新的字符串被存储:cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";
。
关于此方法,没有什么特别的地方;它只是一个在页面级别收集使用数据的例子。也许该数据存储在 Android 应用程序的数据库中会有用。此数据如何回到 Java 代码呢?
要发送字符串对象数组,您调用 JSON.stringify
函数,将数组作为参数传递进来。根据需要,stringify 函数可以允许定制一个复杂对象的特定属性如何被格式化。关于这是如何完成的更多信息,可以参考 json.org 中的解释(参见 参考资料)。
图 3 展示了应用程序的典型运行中解析 JSON 数组之后 Log 中的内容。
图 3. 解析从 JavaScript 发送来的 JSON 数组
本例只存储字符串数据,所以您可以简单地将之附加到一个较长的字符串后面,并调用 CalculatorHandler
中的一个简单函数,然后该函数可以将之解析出来。但是,若是应用程序想要跟踪其他数据(比如某些变量的值)或者甚至试图通过记录特定的函数调用过程来剖析代码,那么情况又是如何呢?显然,在较复杂的情景中,记录和交换对象的能力很重要。
回页首
结束语
本文演示了 Android 应用程序中的 Java 代码与 WebView 中的 JavaScript 代码之间传输数据的技术,以及利用 WebKit 开发的混合应用程序的一些比较普通的主题。混合应用程序混合了 JavaScript、JSON、回调函数、Android-SDK Java 代码以及所有当中最为重要的成份 — 想象力,以交付灵活且功能强大的移动应用程序。
回页首
下载
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本文源代码 | andjson.zip | 58KB | HTTP |
关于下载方法的信息
参考资料
学习
- 在 Android 上使用 XML 和 JSON,第 1 部分:在 Android 应用程序上研究 JSON 和 XML 益处(Frank Ableson,developerWorks,2010 年 7 月):在这个两部分的系列文章中,学习用于在 Android 平台上处理 XML 和 JSON 的技术。第 1 部分介绍了 XML 和 JSON 的基础知识,并构建了一个 Android 应用程序,它解析并显示一个以两种格式提供的 Twitter 状态更新 feed。
- 用 Eclipse 开发 Android 应用程序(Frank Ableson,developerWorks,2008 年 2 月):本教程展示使用 Eclipse 开发 Android 应用程序最简单的方法。
- Android 开发简介(Frank Ableson,developerWorks,2009 年 5 月):简单介绍了 Android 平台,并学习如何编写基本的 Android 应用程序。
- 使用 Android 实现联网(Frank Ableson,developerWorks,2009 年 6 月):探索 Android 的联网功能。
- 在 Android 上使用 XML(Michael Galpin,developerWorks,2009 年 6 月):了解在 Android 上使用 XML 的不同方法,以及如何使用它们构建自己的 Android 应用程序。
- JSON 简介:学习 JSON 语法。
- To XML and Back: Using JSON in Android(Frank Ableson,Linux Magazine,2010 年 3 月):了解更多关于 JSON 和 Android 的信息。
- Under the Hood of Native Web Apps for Android:了解 Android 中的混合应用程序。
- 手机设计和开发 (Brian Fling,O'Reilly Media,2009):在本书中学习制造移动产品的实践指南、标准、技术和最佳实践。
- The Open Handset Alliance:访问 Android 赞助商。
- 本文作者的更多文章(Frank Ableson,developerWorks,2002 年 4 月 — 至今):阅读关于 Android、XML、Eclipse、GPS、BlackBerry 应用程序和其他技术的文章。
- developerWorks 中的 XML 专区:在 XML 专区获取提高您的专业技能所需的资源。
- My developerWorks 中文社区:个性化您的 developerWorks 体验。
- IBM XML 认证:了解如何才能成为一名 IBM 认证的 XML 和相关技术的开发人员。
- XML 技术库:查看 developerWorks XML 专区获得广泛的技术文章和技巧、教程、标准和 IBM 红皮书。阅读更多的 XML 技巧。
- developerWorks 技术活动 和 网络广播:随时关注这些活动中的技术。
- developerWorks 播客:收听面向软件开发人员的有趣访谈和讨论。
获得产品和技术
- Android SDK:从官方 Android 开发人员网站下载 Android SDK. Version 1.5 或更新版进行本文的练习。
- Eclipse:获取最新 Eclipse IDE。
- Android Open Source Project:Android 是开源的,就是说您可以获取源代码。
- JSONLint:在线使用 JSON 验证器。
- IBM 产品评估试用版软件:下载或 在线试用 IBM SOA Sandbox,并开始使用来自 DB2®、Lotus®、Rational® 、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- XML 专区讨论论坛:参与任何一个 XML 相关讨论。
- developerWorks 博客:阅读这些博客并参与讨论。
关于作者
W. Frank Ableson 是一名企业家,同他的妻子和孩子住在新泽西北部,他的专业兴趣包括移动软件和嵌入式设计。他是 揭密 Android 一书(Manning Publications,2010)的作者,也是 Linux Magazine 的移动编辑。
建议
0 条评论 登录添加评论举报不良信息
添加评论
标有星号(*)的是必填项目。
评论:*
显示最新的 5 条评论 显示后 5 条评论 显示所有评论
登录添加评论
快来添加第一条评论