返回博客首页
← 所有文章

如何分析 NativeScript 应用的原生视觉 UI 树和包

2015 年 7 月 17 日 — 作者:Nikolay Diyanov

里面到底有什么?

使用 JavaScript 和 CSS 等 Web 技术编写的应用程序通常被视为 Web 应用或混合应用。NativeScript 允许使用这些 Web 技能,让你可以用你喜欢的语言(JavaScript 或 TypeScript,任君选择)来编写高性能的 *原生* 应用。但是,在 NativeScript 诞生之初,甚至现在它已经到了 v1 版本,我们还是会遇到一些不相信的人,他们会问:“NativeScript 应用真的像在设备上执行的那样原生吗?或者说,它们最终还是混合应用?”

在本文中,我想通过在运行时剖析一个 NativeScript 应用并展示实际创建并在 iOS 和 Android 上执行的 UI 元素来揭示真相,打消这些质疑。我可以告诉你,这些对象确实是各自平台的原生 UI 组件。如果你仍然不相信,请继续阅读。

我不想展示过于复杂的图表,只是想说明基本概念,所以我会剖析我们 快速入门教程 中的 Photo Album 示例应用。我稍微修改了 Photo Album 仓库,让它默认加载两张图片,而不是从相机中获取图片,这样我们就可以立即看到这两张图片在每个平台上是如何表示的。

原生应用包

iOS

如果你在模拟器上运行应用,或者只是构建它,这会创建一个 *.app 文件,它的完整路径看起来像这样:

Photo Album Native Code\platforms\ios\build\emulator\PhotoAlbumNativeCode.app.

这是你在构建应用时创建的应用程序文件。

右键点击它,选择“显示包内容”,你就可以查看应用程序的内容。你会注意到,你可以查看 JavaScript、CSS 和 XML 文件,以及项目的实际可执行文件(隐藏在 metadata-armv7.bin 中),它使用 JS、CSS 和 XML 文件。

ios-show-package-contents

假设我们要在设备上运行该应用。在这种情况下,会创建一个 *.ipa 文件,然后将其部署并执行到设备上。*.ipa 文件创建在:

Photo Album Native Code\platforms\ios\build\device\PhotoAlbumNativeCode.ipa

右键点击 ipa 文件,选择“用“归档实用工具”打开”。这会创建一个“Payload”文件夹,里面包含之前讨论过的应用文件。

ios-device-archive-utility

Android

构建或运行我们的 Android 项目会在以下位置创建一个 *.apk 应用程序文件:

Photo Album Native Code\platforms\android\bin\PhotoAlbumNativeCode-debug.apk

我们来看看里面有什么。我们可以用任何压缩工具打开它,因为从本质上讲,*.apk 文件是一个 zip 文件。所以,在 apk 归档的 *assets\app* 文件夹中,我们有用于创建应用的 JavaScript、XML 和 CSS 文件,一些 NativeScript 服务文件,以及一个使用 JS、XML 和 CSS 运行应用的 dex 文件。

android-contents

运行时对象的原生视觉树

我们刚刚看到,应用包中包含 JS、CSS 和 XML。根据仓库,我们还知道我们有一个 *GridLayout*,它包含一个 *ListView* 和一个 *Button*,其中 ListView 包含两个用 GridLayout 包裹的图片。现在让我们看看它们在运行时是如何转换的。

iOS

emulator-ios

转到 *Photo Album Native Code\platforms\ios*,打开你在那里找到的 *PhotoAlbumNativeCode.xcodeproj* 文件。这是我们 NativeScript 应用程序的 Xcode 项目表示形式,这个项目在你构建 NativeScript 应用时会进行编译。我们现在将使用 Xcode 的一个工具来检查运行时对象的层次结构。

打开 Xcode 项目后,使用 Xcode 的播放按钮运行应用,并指定模拟器设备模型。假设是 iPhone 6。

xcode-play

确保 Xcode IDE 是活动窗口,从顶部菜单中选择“调试 > 查看调试 > 捕获视图层次结构”。

ios-capture-view-hierarchy

瞧!应用程序运行时对象结构被揭示了,并且......正如你所看到的,它全部都是原生 iOS 对象的层次结构。从 UILayoutContainerView 和 UINavigationTransitionView(因为我们默认包含一个 UINavigationController)开始,向下遍历用于 ListView 包装器的 UITableView,继续使用用于显示图片的 UIImageViews。当然,在底部有代表 NativeScript Button 的 UIButton 和 UIButtonLabel。

ios-hierarchy
(点击图片放大)

Android

为了检查原生元素的层次结构,我们可以使用一个名为 monitor 的独立工具,它属于 Android SDK。通常,你可以在你的 *[Android SDK 文件夹]\tools\monitor* 中找到它。

android-emulator 

假设我们已经用 *tns run android --emulator* 运行了应用,并且应用程序正在运行,打开 *monitor* 应用,从左侧面板中选择 *NativeScriptActivity*,它将显示 NativeScript 应用程序的原生对象的层次结构视图。

android-monitor-choose-activity

正如你在下面看到的,我们只有 Android 平台的原生对象 - Android ListView 用于 NativeScript ListView,Android Button 用于 NativeScript Button,Android ImageView 用于 NativeScript Image。还有顶部导航控件的对象。

android-monitor-hierarchy

所以,这就是证据 - 在设备上执行的是一个原生对象的应用 - 没有任何混合内容或 WebViews。下次有人对你说“这些 NativeScript 人,他们做的是混合应用”,就给他们看这篇博客,他们会惊叹的。:)