Google 致力于为黑人社区推动种族平等。查看具体举措

专为无缝体验设计

即使您的应用运行快且响应迅速,某些设计决策仍可能给用户带来问题,原因包括与其他应用或对话框意外互动,无意中丢失数据,意外屏蔽等。为了避免这些问题,有必要了解一下应用的运行环境以及可能影响应用的系统互动。简而言之,您应该努力开发一个能够与系统以及其他应用无缝互动的应用。

常见的无缝体验问题是应用的后台进程(例如,某个服务或广播接收器)弹出对话框来响应某个事件。这看起来似乎是无害的行为,尤其是当您在模拟器上单独构建和测试应用时。但是,当应用在实际设备上运行时,应用在后台进程显示对话框时可能没有用户焦点。因此,应用最终可能会在活跃应用后显示对话框,或者从当前应用获取焦点并在用户的任何当前操作(例如拨打电话)前面显示对话框。这种行为对您的应用或用户无效。

为避免这些问题,应用应使用适当的系统工具(Notification 类)通知用户。利用通知,您的应用可向用户表明有事件发生,也就是在状态栏中显示图标,而非获取焦点并打断用户。

另一个无缝体验问题的例子就是,由于 Activity 无法正确实现 onPause() 和其他生命周期方法,因此无意中丢失了状态或用户数据。或者,如果您的应用公开了专供其他应用使用的数据,则应通过 ContentProvider 公开,而不是通过全局可读的原始文件或数据库等途径公开。

这些示例的共同之处在于它们涉及与系统和其他应用的良好配合。Android 系统会将应用视为松耦合组件的某种联盟,而不是黑盒代码块。这样的话,作为开发者,您就可以将整个系统视为这些组件的更大联盟。这种方式的好处是您可以与其他应用实现完全的无缝集成,因此您在设计自己的代码时也应该体现这一点。

本文档讨论了常见的无缝体验问题以及相应的避免方法。

不要丢弃数据

请注意,Android 是一个移动平台。这似乎是显而易见的,但重要的是要记住,另一个 Activity(例如“来电”应用)随时都可能在您自己的 Activity 上方弹出来。这将触发 onSaveInstanceState() 和 onPause() 方法,并可能导致应用被终止。

如果其他 Activity 出现时用户正在您的应用中编辑数据,您的应用很可能会在应用被终止时丢失该数据,除非您事先保存了正在进行的工作。“Android 方式”就可以做到这一点:接受或编辑输入的 Android 应用应替换 onSaveInstanceState() 方法,并以某种适当的方式保存它们的状态。当用户重新访问应用时,应该能够获取之前的数据。

有些应用能够很好地利用这种行为,典型的示例包括邮件应用。如果其他 Activity 启动时用户正在撰写电子邮件,应用应将撰写中的电子邮件保存为草稿。

不要公开原始数据

就像您不想穿着内衣走在街上一样,您的数据也不应该随意公开。虽然某些类型的应用可以公开给外界读取,但这通常不是最好的方法。公开原始数据需要其他应用了解您的数据格式;如果您更改了该格式,那么其他任何未进行类似更新的应用都会遇到问题。

“Android 方式”是创建一个 ContentProvider,以通过一个明晰的、经过精心设计且可维护的 API 向其他应用公开数据。使用 ContentProvider 就像插入一个 Java 语言接口,以拆分和组合两段紧密耦合的代码。这意味着您可以在不更改 ContentProvider 公开的接口的情况下修改数据的内部格式,这样就不会对其他应用造成影响。

不要打断用户

如果用户正在运行某个应用(例如通话过程中的电话应用),必定是有意为之。因此,除了直接响应来自当前 Activity 的用户输入外,您应该避免衍生 Activity。

也就是说,不要从在后台运行的 BroadcastReceivers 或 Service 调用 startActivity()。这样做会中断当前正在运行的应用,让用户感到恼火。或许更糟糕的是,您的 Activity 可能会成为“击键土匪”,并收到用户提供给上一个 Activity 的部分输入。根据应用的具体操作,这可能是坏消息。

您不应直接从后台衍生 Activity 界面,而应使用 NotificationManager 设置通知。这些通知会显示在状态栏中,用户可以在闲暇时点按这些通知,查看您的应用要向他显示的内容。

(请注意,所有这些建议都不适用于您自己的 Activity 已位于前台的情况:在这种情况下,用户希望看到您的下一个 Activity 响应输入。)

有很多工作要做?在一个线程中完成

如果您的应用需要执行一些代价高或长时间运行的计算,您最好将其移到一个线程中。这样可以防止向用户显示可怕的“应用无响应”对话框,最终导致应用被愤怒的用户终止。

默认情况下,Activity 中的所有代码及其所有视图都在同一个线程中运行。这也是处理界面事件的同一线程。例如,当用户按下某个键时,系统会向 Activity 主线程的队列中添加一个 key-down 事件。事件处理脚本系统需要快速使该事件出列并处理该事件,否则系统会在几秒钟后认为应用已挂起并建议用户终止该应用。

如果您有长时间运行的代码,则在 Activity 中以内联方式运行代码会在事件处理程序线程上运行它,这样实际上屏蔽了事件处理程序。这将延迟输入处理,并导致出现 ANR 对话框。为避免出现这种情况,请将您的计算移至某个线程中。具体操作请参阅这篇为响应能力而设计文档。

不要使单个 Activity 屏幕过载

任何值得使用的应用都可能有多个不同的屏幕。设计界面的屏幕时,请务必使用多个 Activity 对象实例。

根据您的开发背景,您可能会将 Activity 理解为类似于 Java 小程序一样的东西,因为它是应用的入口点。然而,这并不十分准确:Applet 子类是 Java 小程序的单个入口点,而 Activity 应被视为应用的多个潜在入口点之一。您的“主”Activity 和其他任何 Activity 之间的唯一区别就是“主”Activity 恰好是对 AndroidManifest.xml 文件中的“android.intent.action.MAIN”操作感兴趣的唯一 Activity。

因此,在设计应用时,应将您的应用视为 Activity 对象的联盟。从长远来看,这将使您的代码更易于维护,额外的好处是能很好地适应 Android 的应用历史记录和“返回堆栈”模型。

扩展系统主题背景

界面的外观和风格务必要很好地融合在一起。如果应用的界面与用户的预期形成反差,会让用户感到不快。在设计界面时,您应该尽可能避免创建您自己的界面,而应使用主题背景。您可以根据需要替换或扩展主题背景中的某些部分,但至少要从与其他所有应用相同的界面基础开始。有关所有详细信息,请参阅样式和主题背景

将界面设计成可适用于多种屏幕分辨率

不同的 Android 设备支持不同的屏幕分辨率。有些设备甚至可以通过切换到横屏模式等方式即时更改分辨率。请务必确保您的布局和可绘制对象足够灵活,可在各种设备屏幕上正常显示。

幸运的是,这很容易做到。简而言之,您必须提供不同版本的图片(如果您使用了任何图片)用于主要的分辨率,然后设计您的布局以适应各种尺寸。(例如,避免使用硬编码位置,改用相对布局。)如果您完成了这些工作,系统会处理剩下的事情,而您的应用在任何设备上看起来都会很不错。

假定网络速度较慢

Android 设备会提供各种网络连接选项。所有这些选项都会提供某种数据访问配置,但有些配置会比其他配置快。但最低的共同标准是 GPRS,即 GSM 网络的非 3G 数据服务。即使是支持 3G 的设备也会在很多时候使用非 3G 网络,因此,在未来很长一段时间内,慢速网络仍将是现实。

因此,您应将应用设计为最大限度减少网络访问和带宽消耗。您不能假设网络速度较快,因此应始终针对较慢的网络速度进行规划。如果您的用户恰好使用的是更快的网络,那就太好了—他们的体验只会提升。但是,您要避免相反的情况:有些应用在某些时间可用,但在其他时间却慢得令人恼火,要取决于用户在任何特定时刻所处的位置,这种应用可能不受欢迎。

一个潜在问题就是,如果您使用模拟器,很容易陷入此陷阱,因为模拟器使用桌面设备的网络连接。这种网络几乎肯定比移动网络快得多,因此您需要更改模拟器上的设置,使其模拟较慢的网络。您可以在 Android Studio 中通过 AVD 管理器或在启动模拟器时通过命令行选项执行此操作。

不要假定使用的是触摸屏或键盘

Android 将支持各种手机设备类型。最新的说法是,某些 Android 设备将具有完整的“QWERTY”键盘,而另一些则具有 40 键、12 键甚至其他按键配置。同样,某些设备会配备触摸屏,但很多设备都没有。

在构建应用时,请牢记这一点。不要对特定的键盘布局做出假设,除非您真的希望将应用限制在这些设备上使用。

确保节省设备电池电量

如果移动设备一直插在墙壁上充电,便会缺乏移动性。移动设备是由电池供电的,电池充电后的续航时间越长,人们会越满意,尤其是用户。电池电量消耗最大的两个因素是处理器和无线装置;正因如此,应用应执行尽可能少的工作并尽可能少地使用网络。

要最大限度地缩短应用使用处理器的时间,关键在于编写高效代码。为了最大限度地减少使用无线装置时的耗电量,请务必妥善处理错误情况,并且仅获取所需内容。例如,如果网络操作失败,请勿不断地重试。如果失败一次,很可能是因为用户没有接收到网络信号,所以如果您立即尝试,很可能会再次失败;这样做只会浪费电池电量

用户非常聪明:如果您的程序非常耗电,不难预料他们会注意到。此时,您唯一可以确定的是,您的程序很快将被卸载。