0

语境

我目前正在尝试为混合 Android 应用程序设计模型生成器。目标如下:给定任何使用 PhoneGap 开发的混合 Android 应用程序,生成一个描述 UI 状态的 UI 模型(在这种情况下,我将 UI 状态视为等同于 DOM 状态)以及到这些 UI 状态的转换(由动作表示 - 例如,单击 DOM 元素 X)。该模型由有限状态机表示,其中节点是 UI 状态,边缘是转换。

问题

我当前的任务是想出一种方法来确定 DOM 元素是否注册了事件处理程序(现在假设我们只对初始DOM 状态感兴趣)。在这个 StackOverflow 答案的帮助下,我能够提出一个解决方案,它使用 webView.loadURL("javascript:" + ....) 来确定一个元素是否通过使用例如 element.onclick 或通过使用 jQuery 和 Prototype 等库提供的方法。但是,由于同一个 StackOverflow 答案中指定的原因,我目前无法确定一个元素是否通过 addEventListener() 向其注册了一个事件。(再次,为简单起见,假设我们只对在页面加载后、在任何用户交互之前注册的事件感兴趣)。

问题

  1. 我计划采取的方法如下:我计划从 PhoneGap 应用程序中截取 JavaScript 代码。一旦被截获,我将检测代码(例如,使用 Rhino),以便在每次调用 addEventListener() 之后,我将为该调用中涉及的 DOM 元素放置一个“标记”。然后,我会将这个检测代码传递给 WebView,以便加载这个检测的 JavaScript 代码,而不是原始的 JavaScript 代码。通过这样做,我可以确定调用了哪些元素 addEventListener()。这看起来很简单,但问题是,我想不出一种方法来拦截JavaScript 代码以进行检测,并通过检测代码,以便它被加载以代替原始代码。如前所述,是否有任何工具可以让我进行拦截和传递?顺便说一句,我正在使用 Android 模拟器来运行 PhoneGap 应用程序。
  2. 还有其他更简单(或更优雅)的方法我应该考虑吗?
4

1 回答 1

1

我想出的解决方案如下。我创建了一个新的CordovaWebViewClient(将相同的活动和 webView 对象传递给CordovaWebViewClient构造函数),其中onPageStarted()方法被覆盖。onPageStarted()一旦混合应用程序中的 HTML 页面开始加载,代码就会执行。覆盖代码执行以下操作:

  1. 检索刚刚开始加载的页面的 URL,相对于假定所有 HTML/JS/CSS 文件所在的资产文件夹 (file:///android_asset/)。URL 是onPageStarted()的参数之一,因此您只需访问此参数的值即可检索它。示例:如果 HTML 文件位于 assets/www/foo.html 中,则相对 URL 为 www/foo.html
  2. 通过设置输入流来检索 HTML 代码activity.getAssets().open(relativeUrl),其中activity是当前DroidGap活动,relativeUrl是上一步中找到的相对 URL。
  3. 将 HTML 代码解析为一个Document对象(我使用 Jsoup 来执行此操作)。
  4. 准备好您要作为工具添加的 JS 代码(例如,通过将新代码存储在String对象中)并将该新 JS 代码作为新脚本标记的一部分插入到Document对象中。head(在我的例子中,我在 HTML元素的开头插入了新的 script 标签)
  5. 将修改后的对象重新转换Document成String(比如说我们称这个字符串newHTML),调用loadDataWithBaseURL()方法如下:webView.loadDataWithBaseURL("file:///android_asset/www/", newHTML, "text/html", "UTF-8", null). 这实质上会重新加载页面,尽管其中包含检测代码。

然后我将此扩展设置CordovaWebViewClient为 webView 对象的新 webView 客户端(使用webView.setWebViewClient()方法)。最后,我调用webView.reload()了上面的 web 视图客户端更改生效。因此,每次在 Web 视图中加载新的 HTML 页面时,onPageStarted我编写的代码都会执行。

请注意,由于上面的第 5 项使用“file:///android_asset/www/”作为基本 URL,并且 HTML 是通过字符串加载的,因此您还应该在覆盖方法的开头包含一个检查,onPageStarted()以确保刚开始加载的页面不等于基本URL(即检查URL不完全等于file:///android_asset/www/)。否则会遇到死循环。(我也指示当onPageStartedurl 不以 file:///android_asset 开头时不执行这是预期的基本文件夹)。

于 2014-01-08T22:18:01.583 回答