Android 的电池消耗优化 I-优化网络连接

概述:

为了让你的APP成为一个”良民”, 它应该尽量减少对所在设备的电池的耗损. 本文将会介绍如何才能创建一个根据设备的状态来调整自己行为和功能的APP.

通过采取一些措施, 比如分批处理网络请求, 在失去连接的时候禁用后台服务更新, 或者在电池电量低的时候减少更新的频率, 你可以确保将你的APP对电池的寿命影响降到最低, 而不使用户体验打折.

如何降低电量消耗:

你的APP对网络的请求是导致电量消耗的主要原因, 因为它们需要使用移动或者WiFi射频, 这些都是耗电大户. 超出了发送和接收数据包所需要的能量, 这些射频设备只是打开并保持唤醒就会耗费额外的电力. 就算是简单的保持每隔15秒一次的保持开启移动射频, 就足以快速不间断的耗尽电量了.

收集APP的网络流量数据:

一个APP生成的网络流量可以对它运行设备的电池寿命产生显著的影响. 为了优化这些流量的耗电能力, 你需要测量它并找到它的根源. 网络请求可以直接由用户的操作发起, 也可以由代码本身自己发起, 或者从一个跟APP连接的服务器发起.NetworkTraffic tool(是DDMS工具的一部分)让你可以查看你的APP什么时候和如何通过网络实现数据交换的.

标记网络请求:

APP会因为各种原因用到设备的网络硬件. 为了优化你的APP对网络资源的使用, 你必须明白你的APP为何以及需要多高的频率使用网络资源. 以性能分析为目的, 你应该将网络资源的使用分为这些类别:

l  用户发起的网络请求: 比如用户在新闻类APP中刷新新闻列表.

l  APP发起的网路请求: 也就是Android APP代码发起的网络连接, 而不是用来立即满足用户的某些操作, 比如新闻类的APP请求缓存未读的文章.

l  服务器发起的网络请求: 由跟APP相连的服务器发起的网络连接,也不是直接用于满足用户的某些操作, 比如新闻类的APP收到了一个新的可用的Notification.

下面的流程演示了如何将你的APP源代码根据流量种类分为这三种请求类型. Network Traffic tool使用不同的颜色代表每种流量, 所以你可以单独的识别并优化每个流. 这里描述的技术基于APP里的线程来报告流量, 并识别为用户发起, APP发起或者服务器发起.

1.      在你的APP工程中定义三个常量代表三种类型:

publicstatic finalint USER_INITIATED = 0x1000;
public staticfinal int APP_INITIATED= 0x2000;
public staticfinal int SERVER_INITIATED=0x3000;

2.      通过搜索最常用的类来找到你的APP中的网络代码:

a)        在Android Studio中, 选择Edit>Find>Find in Path.

b)        将下面这些字符串粘贴在Text to find域中:

extends GcmTaskService|extends JobService|extendsAbstractThreadedSyncAdapter|HttpUrlConnection|Volley|Glide|HttpClient

c)        检查Regular expression.

d)        检查File mask(s)和类型*.java.

e)        点击Find按钮.

3.      根据你上一步中找到的, 通过setThreadStatsTag(int)方法到每个使用网络资源的执行线程来标记你的app对网络流量的使用, 栗子:

if (BuildConfig.NETWORK-TEST && Build.VERSION.SDK_INT >= 14) {
    try {
        TrafficStats.setThreadStatsTag(USER_INITIATED);
        // make network request using HttpClient.execute()
    } finally {
        TrafficStats.clearThreadStatsTag();
    }
}

注意: 通过这些条件控制代码来确保tag不会被插入到你的产品代码中,根据使用的编译类型来生成APK. 在这里栗子中, BuildConfig.NETWORK-TEST域表示APK处于测试版本的时候才生效.

注意: 这种标记网络流量的方法依赖于你访问和管理网络socket所使用的API. 一些网络库可能不支持TrafficStats. 更多关于使用Network Traffic tool来标记和跟踪网络流量的信息, 可以参考DetailNetwork Usage in DDMS.

配置网络测试版本:

当你运行性能测试的时候, 你的APK应该尽量接近生产版本. 为了实现这个以便进行网络测试, 应该创建一个network-test版本, 而不是使用debug版本.

1.      在Android studio中打开你的APP.

2.      通过修改你的工程的build.gradle文件创建一个可调式的版本用于网络测试, 栗如:

android {
    ...
    buildTypes {
        debug {
            // debuggable true is default for the debug buildType
        }
        network-test { //这个名字好像不太行
            debuggable true
        }
    }
    ...
}

部署网络测试APK:

要部署由network-test版本生成的APK文件:

1.      检查测试设备中的Developer Options选项是开启的. 更多关于如何检查它的信息可以参考UsingHardware Devices.

2.      使用一个USB电缆, 连接你的测试设备到开发电脑上.

3.      在Android Studio中, 选择窗口左侧的Build Variants.

4.      点击Sync Project with Gradle Files来同步.

5.      从列表中选择network-test.

6.      通过选择Run>Debug来将可调式的版本部署到你的设备.

运行网络流量工具:

Android Studio中的NetworkTraffic tool可以帮助你在运行时实时查看你的APP如何使用网络资源的. 要提高您测试的重复性, 你应该通过清理APP数据来以一个已知的初始化状态开始你的APP. 下面的步骤中包含了如何清除所有的APP数据, 包括以前缓存的数据和网络数据. 该步骤将你的APP带回一个必须重新缓存所有缓存数据的状态. 不要跳过它. 要启动Network Traffic tool并可以查看所有的网络请求:

1.      要启动Network Traffic tool, 需要在Android Studio中选择Tools>Android>Android Device Monitor. 当提示需要网络连接的时候,选择允许.

2.      Android Device Monitor窗口中, 点击DDMS按钮并选择NetworkStatistics页. 如果你看不到这个页面, 那么扩大窗口然后在尝试Window>Reset Perspective.

3.      从可调式的列表中选择你的APP, 然后点击Network Statistics页中的Start按钮. 这时可能会在设备上收到允许USB调式的提示, 要选择OK.

4.      使用下面的adb调试命令清除APP数据:

adb shell pm clear package.name.of.app

5.      启动你的APP并运行一个测试用例. 你的用例应该允许APP拥有空闲时间, 也就是用户不跟APP交互的时候, 这样才能让APP和服务器发起的网络连接生效.

6.      通过清除APP数据和再次执行测试用例来重复测试. 你应该重复测试多次以验证性能数据的可重复性.

使用网络流量标记可以帮助你可以以可视化的方法区分每个种类的网络请求, 在这里不同的颜色代表不同类型的网络请求, 如图:

Android 的电池消耗优化 I-优化网络连接_第1张图片

分析网络流量数据:

在前一节中, 你使用流量ID(traffic identifiers)标记了你的APP代码, 运行了数据并收集了数据. 现在该是如何分析它们了.

Efficient use of network resources by anapp is characterized by significant periods where the network hardware is notin use. 在移动设备上, 启动射频来发送和接收数据会导致明显的开销, 并伴随着移动射频长时间激活. 如果你的APP正在访问网络, 你会看到它对网络的访问是聚集在一起的(一段一段的), 当APP没有连接到网络的时候就会出现一段分隔.

图1演示了一段Network Traffic tool测出的不理想的网络流量图. 该APP频繁的发起网络请求. 这些流量之间都只有一小段间歇, 只有在这间歇期间, 射频才能切换到待机状态, 低功耗模式. 该APP的行为看上去像是在持续的访问网络, 这回导致很高的功耗.

Android 的电池消耗优化 I-优化网络连接_第2张图片

图1, 高功耗的网络连接模式.

下面的图2展示了一个优化过的网络连接模式, APP隔段时间才会发送网络请求, 长时间的无网络访问状态使得射频可以保持在低功耗的待机状态. 该图表演示的工作跟图1 是一样的, 但是网络请求被移动并分组, 这样可以让射频在大多数时间里都保持待机状态.

Android 的电池消耗优化 I-优化网络连接_第3张图片

图2, 低功耗的网络请求.

如果你的APP的网络请求看起来是图2中的样子, 那么恭喜, 你可以使用OptimizingGeneral Network Use描述的技巧来进行下一步的优化. 如果网络流量看起来更像图1中的样子, 那么你可能得更加努力的优化你的APP访问网络的频率. 你应该开始分析你的APP的网络流量是由哪种类型产生的.

分析网络流量类型:

当看到你的APP生成的网络流量图的时候, 你需要搞明白流量源自哪里, 这样才能相应的优化它. 如果频繁的网络活动是因为相应用户的操作而发生的, 那么它没什么不对劲的, 但是如果APP并没有在前台, 或者设备还放在口袋里的时候就出现这样的情况, 那么是完全不合理的. 在之前的小节中, 你为不同的流量类型做了标记, 并使用NetworkTraffic tool来收集APP中的数据并将会产生一个这样的图:

Android 的电池消耗优化 I-优化网络连接_第4张图片

图3, 三种不同的网络流量标记: 用户, APP, 服务器.

Network Traffic tool根据之前创建的标记来为流量涂色. 颜色根据之前的这段代码决定:

publicstatic finalint USER_INITIATED = 0x1000;
public staticfinal int APP_INITIATED= 0x2000;
public staticfinal int SERVER_INITIATED=0x3000;

分析用户发起的网络连接:

由用户发起的网络连接是最容易聚集在一起的, 当用户执行某个activity或者请求额外的网络数据的时候, 就会出现. 你在优化用户发起的网络连接的时候, 目标是查找频繁长时间使用网络的模式, 并尝试创建或者增加没有网络访问的区间.

用户访问网络的不可预测性使得对这种类型的网络访问的优化很具有挑战性. 此外, 用户还会在使用APP的时候期待更快速的相应, 所以为了效率而使用的延迟请求对于用户来说几乎是不可接受的. 通常, 你应该将快速相应用户放在高优先级. 这里有一些可以优化这种类型网络访问的方法:

l  提前获取网络数据 – 当用户在APP中执行一个操作的时候, APP应该提前预估用户下一步可能需要请求的数据, 并在单个连接中批量获取它们, 在用户请求它们之前先保存着.

l  检查连接或者监听改变 – 在执行一个更新之前检查网络连接或者监听连接改变.

l  减少连接数 – 使用可以支持以集合方式下载数据的服务器.

注意: 确保你设计的测试场景可以模拟现实的用户操作.

分析APP发起的网络流量:

由APP代码发起的网络流量是你可以显著优化的部分. 在分析你APP的网络活动中, 找到那些闲置的时间段并决定它们是否可以适当的再被扩大些. 尽量增加射频设备的不工作的时间依然是优化的目标. 这是一些用于优化APP发起的网络流量的方法:

批处理并定时处理网络请求– 推迟你的APP的网络请求, 以便它们可以被一起同时处理, 这样可以有效的降低电池消耗.

允许系统检查连接– 在APP睡眠的时候允许系统检查网络连接.

分析服务器发起的网络流量:

这也是可以显著优化的地方. 我们可以这么做:

使用GCM用于服务器更新– 考虑为服务端更新使用Google云消息服务而不是轮询.

常规网络优化:

压缩数据: 减少发送或者接受的字节数同样可以减少连接的时间, 这可以减少电池的消耗, 你可以:

压缩数据, 使用比如GZIP压缩这样的压缩技术.

使用简洁的数据协议, 比如JSON和XML提供了阅读的便利性以及语言灵活性, 它们是带宽重量级的格式, 在Android平台中需要较高的序列化成本. 二进制序列化格式, 比如Protocol Buffers和FlatBuffers提供了更小的线上(on-the-wire)包大小, 以及更快的编码和解码时间. 如果你的APP定期需要转化大量的序列化数据, 这些格式则可以提供很多的解码时间和转化空间上的收益.

本地缓存数据: 你的APP可以通过缓存来避免下载重复的数据. 应该总是缓存那些静态的资源, 包括那些按需下载比如完整大小的图片, 并在合理的时间内持续的缓存它们. 比如你应该考虑对由用户发起的请求来作为屏幕主要内容的APP来执行这种方法. 当用户首次打开这个屏幕, APP应该显示一个启动画面(splash screen). 随后显示一个之前缓存的数据. 在新数据请求完毕之后再重新加载页面. 更多关于缓存的信息可以参考CacheFiles Locally.

 

参考: https://developer.android.com/training/performance/battery/network/action-any-traffic.html

 

你可能感兴趣的:(Android,性能)