专为无缝体验设计

即使您的应用速度快且响应迅速,某些设计决策仍可能给用户带来问题,原因包括与其他应用或对话框发生意外交互、数据意外丢失、意外阻塞等。为避免这些问题,了解应用的运行环境以及可能影响应用的系统交互会有所帮助。简而言之,您应努力开发能够与系统以及其他应用无缝交互的应用。

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

为避免这些问题,您的应用应使用适当的系统工具(Notification 类)来通知用户。借助通知,您的应用可以通过在状态栏中显示图标来向用户表明事件已发生,而不是获得焦点并打断用户。

另一个无缝性问题的例子是,activity 因未正确实现 onPause() 和其他生命周期方法而意外丢失状态或用户数据。或者,如果您的应用公开了供其他应用使用的数据,您应该通过 ContentProvider 公开,而不是(例如)通过全局可读的原始文件或数据库进行公开。

这些示例的共同之处在于,它们涉及与系统和其他应用的良好协作。Android 系统旨在将应用视为松散耦合组件的一种联合,而不是黑盒代码块。这样,作为开发者,您就可以将整个系统视为这些组件的更大联合体。这样做的好处是,您可以与其他应用进行干净、无缝的集成,因此您在设计自己的代码时应顺应这一点。

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

不要丢弃数据

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

如果其他 activity 出现时用户正修改您应用中的数据,您的应用很可能会在应用被终止时丢失相应数据。除非您事先保存了正在进行的工作。“Android 方式”就是为了做到这一点:接受或修改输入的 Android 应用应替换 onSaveInstanceState() 方法,并以某种适当的方式保存其状态。当用户再次访问该应用时,她应该能够检索她的数据。

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

不要公开原始数据

如果您不会穿着内衣走在街上,您的数据也一样。虽然可以将某些类型的应用公开给所有人读取,但这通常并不是最佳做法。公开原始数据需要其他应用了解您的数据格式;如果您更改该格式,则会破坏所有其他未进行类似更新的应用。

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

不要打断用户

如果用户正在运行某个应用(例如通话期间的“电话”应用),那么可以肯定的是,该用户是有意为之。因此,除了直接响应来自当前 activity 的用户输入之外,您应避免生成 activity。

也就是说,请勿从在后台运行的 BroadcastReceiver 或 Service 调用 startActivity()。这样做会中断当前正在运行的应用,让用户感到恼火。甚至更糟糕的是,您的 Activity 可能会成为“按键老虎机”,并收到用户在向上一个 Activity 提供的过程中输入的一些输入。根据应用的运行情况,这可能是个坏消息。

您应改用 NotificationManager 设置通知,而不是直接从后台生成 activity 界面。这些通知会显示在状态栏中,用户随后可以在空闲时点击它们,查看您的应用需要向他显示的内容。

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

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

如果您的应用需要执行一些昂贵或长时间运行的计算,您应将其移至线程。这样可以防止向用户显示可怕的“应用无响应”对话框,最终导致应用的严重终止。

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

如果您有长时间运行的代码,如果在 Activity 中以内嵌方式运行该代码,则会在事件处理脚本线程上运行该代码,从而有效地屏蔽事件处理脚本。这会延迟输入处理,并导致出现 ANR 对话框。为避免出现这种情况,请将计算移至线程。具体方法请参见本响应性设计文档。

不要使单个 Activity 屏幕过载

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

根据您的开发背景,您可以将 Activity 解读为类似于 Java 小程序,因为它是应用的入口点。不过,这并不十分准确:当小程序子类是 Java 小程序的单个入口点,而 activity 应被视为应用的多个可能入口点之一。您的“主”activity 与其他 activity 之间的唯一区别是,“主”activity 恰好是唯一对 AndroidManifest.xml 文件中的“android.intent.action.MAIN”操作感兴趣的一个 activity。

因此,在设计应用时,请将您的应用视为 Activity 对象的联合。从长远来看,这将提高代码的可维护性;此外,还具有一个好的副作用,即与 Android 的应用历史记录和“返回堆栈”模型相得益彰。

扩展系统主题背景

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

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

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

幸运的是,这很容易实现。简而言之,您必须提供不同版本的图形(如有使用)用于关键分辨率,然后设计布局以适应各种尺寸。(例如,避免使用硬编码位置,而使用相对布局。)如果您完成了很多工作,系统会处理剩下的事情,而您的应用在任何设备上都能完美呈现。

假定网络速度较慢

Android 设备会提供各种网络连接选项。所有 API 都会提供一些数据访问权限配置,但有些配置会比其他配置更快。不过,最低的共同点是 GPRS,即 GSM 网络的非 3G 数据服务。即使支持 3G 的设备也会在非 3G 网络上花费很多时间,因此在很长一段时间内,慢速网络仍将是现实。

因此,您应始终对应用进行编码,以最大限度减少网络访问量和带宽用量。您不能假定网络速度较快,因此应始终针对慢速做好规划。如果您的用户恰好使用的是更快的网络,那就太好了,他们的体验只会提升。但您应避免反过来:某些应用有时可用,但根据用户在任何给定时刻所处的位置导致运行速度缓慢(令人沮丧)很可能不受欢迎。

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

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

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

在构建应用时,请牢记这一点。不要对特定的键盘布局做出假设 - 当然,除非您真的想要限制自己的应用,使其只能在此类设备上使用。

确保节省设备电池电量

如果移动设备一直插在墙壁上,便不是移动设备的好选择。移动设备是由电池供电,电池充满电的时间越长,每个人(尤其是用户)就越满意。电池电量消耗最大的两个因素是处理器和无线装置;因此,开发者务必要编写尽可能少的工作,并尽可能少使用网络。

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

用户非常聪明:如果您的程序非常耗电,您可以指望他们注意到。此时,您唯一可以确定的是,您的程序将无法长时间保持安装状态。