1、流量消耗小
2、电量消耗小
3、用户等待时间短
属于Android内置Monitor工具,实时跟踪选定应用的数据请求情况. 我们可以连上手机, 选定调试应用进程, 然后在App上操作我们需要分析的页面请求.
一般来说, 网络代理工具有两个作用:
减少Radio活跃时间
也就是减少网络数据获取的频次.这就减少了radio的电量消耗, 控制电量使用.
减少获取数据包的大小
可以减少流量消耗也可以让每次请求更快, 在网络情况不好的情况下也有良好表现, 提升用户体验.
适当的缓存, 既可以让我们的应用看起来更快, 也能避免一些不必要的流量消耗.
当接口设计不能满足我们的业务需求时. 例如可能一个界面需要请求多个接口, 或是网络良好, 处于Wifi状态下时我们想获取更多的数据等.
这时就可以打包一些网络请求, 例如请求列表的同时, 获取Header点击率较高的的item项的详情数据.
通过监听设备的状态:休眠状态、充电状态、网络状态
结合JobScheduler来根据实际情况做网络请求. 比方说Splash闪屏广告图片, 我们可以在连接到Wifi时下载缓存到本地; 新闻类的App可以在充电, Wifi状态下做离线缓存.
除了正常的网络优化, 我们还需考虑到弱网情况下, App的表现.创建和启动Android模拟器可以设置网络速度和延迟
弱网优化, 本质上是在弱网的情况下能让用户流畅的使用我们的App. 我们要做的就是结合上述的优化项:
包括服务器端的代码开发, 部署方式等
HTTPS 在保护用户隐私,防止流量劫持方面发挥着非常关键的作用,但与此同时,HTTPS 也会降低用户访问速度,增加网站服务器的计算资源消耗。
影响主要来自两方面:
a、协议交互所增加的网络 RTT(round trip time)。
b、加解密相关的计算耗时。
HTTP 首个请求的网络耗时,用户只需要完成 TCP 三次握手建立 TCP 连接就能够直接发送 HTTP 请求获取应用层数据,此外在整个访问过程中也没有需要消耗计算资源的地方。HTTPS 的访问过程,相比 HTTP 要复杂很多,在部分场景下,使用HTTPS 访问有可能增加 7 个 RTT。
密钥交换时加密解密过程,需要非常消耗CPU资源的计算耗时
a.Batterystats & bugreport
b.Battery Historian
Android系统本身为了优化电量的使用, 会在没有操作时进入休眠状态, 来节省电量.
可以用WakeLock来保持CPU运行, 或是防止屏幕变暗/关闭, 让手机可以在用户不操作时依然可以做一些事儿(如播放视屏);或者灭屏后执行其他操作。
(见网络优化)
(1)WakeLock获取释放成对出现
(2)使用超时WakeLock,以防异常导致没有没有释放
// Acquires the wake lock with a timeout.
acquire(long timeout)
BatteryManager会发送一个包含充电状态的持续广播, 我们可以通过此广播获取充电状态和电量详情:
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
注意: 因为这是一个持续广播, 我们无需写receiver, 可以直接通过intent获取相关数据.
(1)通过Intent获取充电状态和电量详情
例如, 如果设备正在充电:
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
(2)注册receiver监听充电状态变化
只要设备连接或断开电源, BatteryManager就会广播相应的操作, 我们可以注册receiver来监听:
监听电池状态, 可以让我们将一些操作放在充电或是电量足够的情况下进行, 以提升用户体验. 例如用户数据同步, Log上传等.
Android 6.0提供了两个用来节省电量的技术Doze和App Standby.
(1)Doze
瞌睡. 如果设备闲置了一段较长时间, Doze技术将通过延迟后台网络活动, CPU运行等来减少电量损耗.
(2)App Standy
应用待机. 不是最近得到过用户"宠幸"的App, App Standy将延缓这个应用的后台网络活动.
(1)定位中使用GPS, 请记得及时关闭
// Remove the listener you previously added
locationManager.removeUpdates(locationListener);
(2)减少更新频率
(3)根据实际情况选择GPS或网络或两者. 只使用一个会降低电量损耗.
ANR全名Application Not Responding, 也就是"应用无响应".
在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下两种情况下会弹出ANR对话框:
(1)5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
(2)BroadcastReceiver在10s内无法结束.
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.
不要在主线程(UI线程)里面做繁重的操作.
ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:
$adb pull data/anr/traces.txt .
- locked <0x35fc9e33> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait
CPU占用100%,满负荷,觉得多数是被IOwait,即IO操作占用。此时分析方法调用栈, 一般来说会发现是方法中有频繁的文件读写或是数据库读写操作放在主线程来做了.
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732//free内存所剩无几
开辟单独的子线程来处理耗时阻塞事务.
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.
增大VM内存, 使用largeHeap属性, 排查内存泄露(内存优化)等.
以预防为主,有主线程和子线程概念,认清代码的阻塞点,善用线程。
思想:不要在主线程(UI线程)里面做繁重的操作.
继承Thread
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeThread p = new PrimeThread(143);
p.start();
实现Runnable接口
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
AsyncTask开启异步任务
private class DownloadFilesTask extends AsyncTask {
// Do the long-running work in here
// 执行在子线程
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
// This is called each time you call publishProgress()
// 执行在主线程
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// This is called when doInBackground() is finished
// 执行在主线程
protected void onPostExecute(Long result) {
showNotification("Downloaded " + result + " bytes");
}
}
// 启动方式
new DownloadFilesTask().execute(url1, url2, url3);
Android中结合Handler和Thread的一种方式. 默认情况下Handler的handleMessage是执行在主线程的, 但是如果我给这个Handler传入了子线程的looper, handleMessage就会执行在这个子线程中的. HandlerThread正是这样的一个结合体:
// 启动一个名为new_thread的子线程
HandlerThread thread = new HandlerThread("new_thread");
thread.start();
// 取new_thread赋值给ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 此时handleMessage是运行在new_thread这个子线程中了.
}
}
Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.
Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.
// 使用LoaderManager来初始化Loader
getLoaderManager().initLoader(0, null, this);
//如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
//如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器
// 创建一个Loader
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
// 加载完成
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
Android本身给我们提供了很多App性能测试和分析工具, 而且大部分都集成到Android Studio或DDMS中, 非常方便使用.
1.1 StrictMode
(1)说明
顾名思义, “严格模式”, 主要用来限制应用做一些不符合性能规范的事情. 一般用来检测主线程中的耗时操作和阻塞. 开启StrictMode后, 如果线程中做一些诸如读写文件, 网络访问等操作, 将会在Log console输出一些警告, 警告信息包含Stack Trace来显示哪个地方出了问题.
(2)作用
主要用来做主线程优化分析
1.2 Systrace
(1)说明
Systrace是一个收集和检测时间信息的工具, 它能显示CPU和时间被消耗在哪儿了, 每个进程和线程都在其CPU时间片内做了什么事儿. 而且会指示哪个地方出了问题, 以及给出Fix建议.
其以trace文件(html)的方式记录. 可以直接用Chrome浏览器打开查看. 界面如下:
(2)作用
用来分析UI的绘制时间, 结合Hierarchy Viewer来提升UI性能.也可以用来发现耗时操作.
1.3 Hierarchy Viewer
(1)说明
Hierarchy Viewer提供了一个可视化的界面来观测布局的层级, 让我们可以优化布局层级, 删除多余的不必要的View层级, 提升布局速度.
有必要说明下的是:
上图红框标出的三个点是关键分析数据. 左起依次代表View的Measure, Layout和Draw的性能. 另外颜色表示该View的该项时间指数, 分为:
2.1 Google的Battery Historian
(1)说明
Google出品, 通过Android系统的bugreport文件来做电量使用分析的工具.
(2)作用
用来做电量使用分析.
2.2 Emmagee(网易)
(1)说明
针对Android App的CPU, 内存, 网络, 电量等多项综合的测试分析.
(2)作用
比官方工具更适合国人使用来做App的整体性能分析.
2.3 leakcanary
(1)说明
Square出品, 必属精品.类似与App探针的内存泄露监测工具.
(2)作用
集成到App中, 用来做内存问题预防最好不过了.