在 Android 平台上启动 Tizen 应用程序
PUBLISHED
先决条件
本文所附的所有应用程序都需要在 Android 版本 2.2 或更高版本的 Android 设备上运行。
简介
在 Android 平台上启动 Tizen HTML5 应用程序之前,您需要遵循在“保持高的 Tizen web 应用程序的可移植性”的文章中详细描述的可扩展应用程序 UI 的开发流程。 如果您不遵循这些步骤,应用程序的 UI 将不会在各种 Android 设备上缩放。 您会认为 UI 缩放不是强制性的,因为它可以通过 Android 平台本身完成。 事实上,我们不建议这么做,因为 Android 在放大或缩小应用程序规模时可能会产生不良后果。
在 Android 平台上启动 HTML5 的应用程序(Tizen 的 Web 应用程序)的方法有很多。 最常见的是:
- 要建立一个项目,请使用 PhoneGap/Cordova。 使用这种方法可以创建一个 HTML5 应用程序,且 PhoneGap 会为 Android、iOS、Windows Mobile、bada 等不同的硬件平台自动创建一个本机构造。 PhoneGap 提供其自身的 API,为 JavaScript 和本机平台 API 之间搭建桥梁。
- 创建您自己的包装应用程序并在其内嵌入 HTML5 应用程序。
- 使用某些框架来创建 HTML5 应用程序。 通常这些框架会提供一种工具来构建一些本机平台的应用程序 — 包括 Android 在内。 这一框架的一个好示例是 Sencha Touch。
在这篇文章中,我们将指导您如何使用第 1 和第 2 种方法,以便在 Android 设备上启动以前开发的 Earth Guard 1.0.3 应用程序。 您可以找到附加到这篇文章的两个项目:
- EarthGuardPhoneGapExample,对应第一种方法。
- AndroidEarthGuard,对应第二种方法
我们还将尝试指出两者的优缺点。
对于第一种和第二种方法,在 Android 本机应用程序中嵌入的 HTML5 应用程序背后的概念是类似的。 唯一的区别是 PhoneGap 为本机 Android API 类别提供作为包装程序的类别。
HTML5 应用程序被嵌入到一个 WebView 对象实例中,位于 Android 活动内部。 HTML5 应用程序可以使用 WebAppInterface 类的一个实例与 Java Android 包装器进行通信。 通过 WebView 进行反向通信是可能的。
图 1: 在 Android 应用程序中嵌入的 HTML5 应用程序背后的基本概念。
图 2: 三星 Galaxy S3 上的 Earth Guard 应用程序
使用 PhoneGap/Cordova 为 Android 系统构建一个 Tizen HTML5 应用程序
我们认为开始使用 PhoneGap 的最简单方式是使用随附在 PhoneGap 包的示例蓝图应用程序。 您可以从下载站点下载最新的 Cordova 程序包。 PhoneGap 的 Android 示例应用程序可在 /lib/android/example 中找到。
如果您决定使用蓝图示例作为您的应用程序的起始点,请遵循以下步骤:
- 请确保您安装了Java、Android SDK 和 Ant。 安装这类软件不在本文的讨论范围内。
- 请更新您的环境变量 — 路径以包括下列程序: android、 ant、 adb。
- 在您 PhoneGap 目录中转到./phonegap/lib/android,并将示例文件夹复制到任何位置。
- 将 ./example/assets/www 重命名为 ./example/assets/www_org
- 创建新目录./example/assets/www。
- 将 HTML5 应用程序复制到 ./example/assets/www 。 此时唯一的要求是要有一个有效的 HTML5 index.html 文件。 无需任何 CSS 或 JavaScript 文件。
- 移动到 ./example/cordova 目录
- 在 shell 中执行以下命令: android 更新项目-p../ — — 目标 android-17
- 清理项目 ./clean
- 构建项目 ./build
- 连接 Android OS > = 2.1 的 Android 设备
- 启动应用程序:./run
- 现在您应该启动了您的应用程序并在 Android 设备上运行。 您的 index.html 文件将呈现在屏幕上。
不幸的是这只是一次长途旅行的开始,在您的 Android 设备上完全运行 Earth Guard 游戏后才会结束。
您应将 Android 项目导入到 Android 的 Eclipse IDE。 为了做到这一点,请按照下列步骤操作:
- 安装 Android SDK 并启动 Eclipse IDE
- 从“现有代码”转至“文件” - >“新建” - >“项目” - >“Android 项目”,并导入您以前创建的 Android PhoneGap 应用程序(示例应用程序)
- 从 Package Explorer 的上下文菜单中选择 cordovaExample 项目,然后转到“属性”-> “Android”即可选择可用的最新 Android API 版本。 我选择的是 Android 4.2
您现在可以从 Eclipse IDE 开发、调试和部署 Android PhoneGap 应用程序。
您可以使用 JavaScript 应用程序内的 console.log 调用对 Android PhoneGap 应用程序进行调试。 您应观察带 cordova 标签的 LogCat ,筛选调试消息。 很抱歉,您不能看到和探索对象的完整属性,LogCat 上只有简单的字符串消息可用。
此时您的应用程序不能使用 PhoneGap API 的任何一部分,所以您只用了它一半的功能。 PhoneGap API 的完整文档可以在此处找到。 下一步是在您的 index.html 中包括 cordova-2.5.0.js 文件。 这使您可以在应用程序中使用 PhoneGap API。 您只需要在您的应用程序源代码中执行一项强制性变更 — deviceready 事件侦听器注册。 请参考./assets/www/js/main.js 文件:
document.addEventListener("deviceready", function() { if (view.checkEnviroment()) { game.inputHandler = inputHandler(); levelManager.init(); view.resize(); view.setPowerState("SCREEN_BRIGHT"); game.initialize(); } }, true);
deviceready 事件完成初始化后,即被 Cordova WebView 触发,因此,在此事件被触发后,HTML5 应用程序中的每项操作都应完成。 否则,您将无法使用 PhoneGap API 的任何部分。
很抱歉,这是强制性操作,但为了提供对 Android 系统上 Earth Guard 的完整用户体验,我们应该:
- 处理事件的生命周期
- 操作设备后退按钮
- 操作退出按钮
- 块方向变化 — 示例应用程序不支持横向视图
处理应用程序生命周期和操作后退按钮
处理应用程序的生命周期和操作后退按钮只是在 phoneGapHandler 模块中绑定相应的事件 (./ assets/www/js/modules/phoneGapHandler.js)。 请参阅下面的代码:
/** * Module responsible for handling all phoneGap functionalities for application */ "use strict"; var phoneGapHandler = function() { var _init = function() { /** * Cordova back button handler */ document.addEventListener("backbutton", menu.toggle, false); /** * Cordova application life cycle handling */ document.addEventListener("pause", game.onHidden, false); document.addEventListener("resume", game.onVisible, false); }; return { init : _init }; }();
操作退出按钮
您需要提供单独的 PhoneGap API 调用以关闭该应用程序。 请参阅游戏模块中的退出方法 (./ assets/www/modules/game.js):
exit : function() { // Tizen exit handler if (typeof tizen !== "undefined" && tizen.application) { if (confirm("Exit?")) { var app = tizen.application.getCurrentApplication(); app.exit(); } // PhoneGap exit handler } else if (typeof navigator.app.exitApp !== "undefined") { navigator.app.exitApp(); } else { alert("Not supported"); } },
块方向变化
App 方向可以被固定到 AndroidManifest.xml 文件中的文字描述。 请在下面找到合适的代码:
"cordovaExample" android:label="@string/app_name" android:theme="@android:style/Theme.Black.NoTitleBar" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:screenOrientation="portrait">
使用您自己的 Java 包装器为 Android 系统构建 Tizen HTML5 应用程序
这种方法有点困难,但可为您提供更大的灵活性。 您可以决定哪些 Android 的 API 在 HTML5 应用程序内应用,反之亦然,但您需要自己执行。 我们将不详细描述如何创建一个新的 Android 项目,以及如何添加 WebView 和 WebAppInterface 到 MainActivity。 可在 http://developer.samsung.com/android 上找到关于这些问题的单独教程。
如果您决定为 HTML5 应用程序构建自己的 Java Android 包装器,您需要考虑以下问题:
- 在 Java 包装器和 HTML5 应用程序之间提供一个接口,反之亦然
- 处理来自 JS 应用程序的 console.log 消息并将它们转发到 LogCat
- 操作后退按钮
- 处理 Android 应用程序生命周期
- 处理电源管理
在 Java 包装器和 HTML5 应用程序之间提供接口,反之亦然
我们可以提供 Java 应用程序与 HTML5 应用程序之间的双向通信。 由我们自己决定 Android API 的哪些部分应被发布到 HTML5 应用程序。 在我们的例子中,此 API 在 ./src/com/Samsung/srpol/androidearthguard/WebAppInterface.java 文件中被定义:
/** * Method available in JS application */ @JavascriptInterface public void exitApplication() { mActivity.finish(); } /** * Method available in JS application */ @JavascriptInterface public void setScreenOn(boolean screenOn) { mActivity.setScreenOn(screenOn); }
这两种方法在 Android 命名空间中的 HTML5 应用程序内可用。 此命名空间在 MainActivity 中的 Android 包装器内被定义:
@SuppressLint("SetJavaScriptEnabled") private void initWebView() { … // Provides Android namespace in JS application mWebView.addJavascriptInterface(mWebAppInterface, "Android"); … }
我们在游戏模块中的 HTML5 应用程序内使用在命名空间定义的方法:
exit : function() { … Android.exitApplication(); … },
在 View.js 模块中:
setPowerState : function(type) { … Android.setScreenOn((type === "SCREEN_BRIGHT") ? true : false); … },
来自 HTML5 的各种方法可以在 Java 包装器内被调用,为此,您应使用 WebView.loadUrl 方法。
一个例子包括在 ./ src/com/Samsung/srpol/androidearthguard/MainActivity.java:
public boolean onKeyDown(int keyCode, KeyEvent event) { // Back button handler if ((keyCode == KeyEvent.KEYCODE_BACK)) { Log.d(this.getClass().getName(), "back button pressed"); mWebView.loadUrl("javascript:menu.toggle()"); return false; } return super.onKeyDown(keyCode, event); }
处理来自 JS 应用程序的 console.log 消息并将它们转发到 LogCat
在没有合适的调试机制时,我们的 HTML5 应用程序中的 console.log 消息不会显示在 LogCat 上。 您需要将 console.log 消息转发到 LogCat:
mWebView.setWebChromeClient(new WebChromeClient() { public boolean onConsoleMessage(ConsoleMessage cm) { Log.d(getString(R.string.js_console_tag), "[EarthGuard]: " + cm.message() + " -> line(" + cm.lineNumber() + ") of " + cm.sourceId()); return true; } });
来自该示例应用程序的每条消息都用 [EarthGuard] 字符串进行了标记,所以您可以使用 LogCat 中的标签框轻松筛选消息。
操作后退按钮
由您来决定是否希望操作应用程序中的后退按钮。 在 Earth Guard 示例应用程序中,我们决定将一个菜单切换方法附加到 Android 后退按钮。 请参阅下面的代码:
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Back button handler if ((keyCode == KeyEvent.KEYCODE_BACK)) { Log.d(this.getClass().getName(), "back button pressed"); mWebView.loadUrl("javascript:menu.toggle()"); return false; } return super.onKeyDown(keyCode, event); }
处理 Android 应用程序生命周期
Android 系统活动的生命周期很复杂 — 详见此处。 幸运的是,我们的 HTML5 应用程序只应处理一部分:
- 当“活动”被发送到后台时,我们应调用 game.onHidden() 方法
- 当“活动”被发送到前台时,我们应调用 game.onVisible() 方法
请参阅下面的代码:
/** * Triggers game.onVisible() method in JS application * */ @Override protected void onResume() { super.onResume(); Log.d(this.getClass().getName(), "Main activity onResume()"); mWebView.loadUrl("javascript:game.onVisible()"); if (mScreenOn) { mWakeLock.acquire(); } } /** * Triggers game.onHidden() method in JS application * */ @Override protected void onPause() { super.onPause(); Log.d(this.getClass().getName(), "Main activity onPause()"); mWebView.loadUrl("javascript:game.onHidden()"); if (mWakeLock.isHeld()) { mWakeLock.release(); } }
处理电源管理
为了防止屏幕变暗,在玩游戏时,您必须执行电源管理。 在 Earth Guard 游戏里,使用了 PowerManager 类及其唤醒锁。 在清单文件中需要一个特别权限:
"android.permission.WAKE_LOCK" />
使用唤醒锁会大大提高功率消耗,因此在必要时才应使用。 在 Earth Guard 游戏中,我们希望在用户玩游戏时防止屏幕变暗。 关于电源管理的所有逻辑都在 MainActivity 中。 来自 JavaScript 部分的唯一可用函数是 setScreenOn(Boolean screenOn) 方法。
/** * Method available in JS application. Switch on/off preventing the screen from dimming * * @param screenOn * true if we want to prevent the screen from dimming, false if we want to release it */ @JavascriptInterface public void setScreenOn(boolean screenOn) { mActivity.setScreenOn(screenOn); }
它调用 MainActivity 的 setScreenOn() 方法,稍后将进行介绍。
在执行电源管理的同时,我们添加了两个附加字段到我们的 mainActivity 类:
private PowerManager.WakeLock mWakeLock; private boolean mScreenOn = false;
第一个,mWakeLock,是一个 WakeLock 对象,该对象将被使用。 第二个是一个标志,表示屏幕是否应保持亮度。 由上文所述的 setScreenOn 函数设置它的值。
使用唤醒锁相当简单。 您必须创建 WakeLock 类的一个实例,然后使用两个可用的函数:
- acquire(),
- release()。
protected void onCreate(Bundle savedInstanceState) { ... PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MainActivity"); ... }
NewWakeLock(int levelAndFlags,字符串标记) 函数获得两个参数。 第一个参数指定唤醒锁级别和可选标志。 可能有 4 个级别:
- PARTIAL_WAKE_LOCK,
- FULL_WAKE_LOCK,
- SCREEN_DIM_WAKE_LOCK,
- SCREEN_BRIGHT_WAKE_LOCK
在 EarthGuard,我们使用 SCREEN_BRIGHT_WAKE_LOCK 标志。 它确保屏幕已开启,并保持最高亮度,不能变暗。 在 newWakeLock 函数中的第二个参数是用于调试目的的一个标志。
我们现在可以使用 acquire() 和 release() 函数。
public void setScreenOn(boolean screenOn) { mScreenOn = screenOn; if (screenOn && !mWakeLock.isHeld()) { mWakeLock.acquire(); } else if (!screenOn && mWakeLock.isHeld()) { mWakeLock.release(); } }
SetScreenOn 方法采用一个参数,即 screenOn。 当我们想要防止屏幕变暗 (screenOn 值为 true) 时,我们使用 mWakeLock.acquire () 方法。 我们这样做之前,我们必须检查是否使用了 mWakeLock.isHeld () 方法使唤醒锁处于不活动状态。 要释放唤醒锁,我们使用 mWakeLock.release () 方法。
当应用程序进入后台及被恢复时,妥善处理唤醒锁同样重要。
@Override protected void onResume() { super.onResume(); ... if (mScreenOn) { mWakeLock.acquire(); } } @Override protected void onPause() { super.onPause(); ... if (mWakeLock.isHeld()) { mWakeLock.release(); } }
在 onResume() 中,我们检查是否应打开唤醒锁(mScreenOn 标志被设为 true),如果是,则获取它。 在 onPause() 函数中,我们检查唤醒锁是否处于活动状态,如果是,则将其释放。
总结
在 HTML5 应用程序中 |
只有 Android 设备 API 可用 |
Android 的最低版本 |
建议的 Android 版本 |
从 Java 到 HTML5 应用程序的接口 |
|
专用的 Java 包装器 |
是 |
否(*) |
2.2 API8 |
4.2 API17 |
是 |
PhoneGap/Cordova 方法 |
否 |
是 |
2.1 API7 |
4.2 API17 |
否 |
PhoneGap 方法与专用的包装器方法的比较表
(*) — 您应自行提供
我们认为,在某些情况下,专用的 Java 包装器比 PhoneGap 更好:
- 如果您决定仅在 Android 设备上启动 HTML5 应用程序
- 如果您希望提供一些特定功能,将 Android API 引入 HTML5 应用程序
在任何其他情况下,我们建议您使用 PhoneGap 方法。