第八章 性能优化 之 其他优化(四)

第八章 性能优化 之 其他优化(四)

(一)网络优化

(1)网络优化的必要性

1、流量消耗小
2、电量消耗小
3、用户等待时间短

(2)分析网络连接的工具

1、Network Monitor

属于Android内置Monitor工具,实时跟踪选定应用的数据请求情况. 我们可以连上手机, 选定调试应用进程, 然后在App上操作我们需要分析的页面请求.

2、网络代理工具

一般来说, 网络代理工具有两个作用:

  • 截获网络请求响应包, 分析网络请求
  • 设置代理网络, 移动App开发中一般用来做不同网络环境的测试, 例如Wifi/4G/3G/弱网等.
    代理工具很多, 诸如Wireshark, Fiddler, Charles等

(3)网络连接的优化

1、思想

减少Radio活跃时间
也就是减少网络数据获取的频次.这就减少了radio的电量消耗, 控制电量使用.
减少获取数据包的大小
可以减少流量消耗也可以让每次请求更快, 在网络情况不好的情况下也有良好表现, 提升用户体验.

2、设计

(1)接口设计
  • API设计
    App与Server之间的API设计要考虑网络请求的频次, 资源的状态等. 以便App可以以较少的请求来完成业务需求和界面的展示.
  • Gzip压缩
    使用Gzip来压缩request和response, 减少传输数据量, 从而减少流量消耗.
  • 使用Protocol Buffer代替JSON
    Protocol Buffer是Google推出的一种数据交换格式.如果我们的接口每次传输的数据量很大的话, 可以考虑下protobuf, 会比JSON数据量小很多.
    JSON也有其优势, 可读性更高.减少数据量(当然还有映射成POJO的方便程度)
  • 图片的Size
    图片相对于接口请求来说, 数据量要大得多. 故而也是我们需要优化的一个点.我们可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费.
(2)网络缓存

适当的缓存, 既可以让我们的应用看起来更快, 也能避免一些不必要的流量消耗.

  • 有网时根据设置的Cache Control时间来判断是使用缓存还是重新做网络请求.
  • 无网络环境下直接使用缓存, 保证阅读体验.
(3)打包网络请求

当接口设计不能满足我们的业务需求时. 例如可能一个界面需要请求多个接口, 或是网络良好, 处于Wifi状态下时我们想获取更多的数据等.
这时就可以打包一些网络请求, 例如请求列表的同时, 获取Header点击率较高的的item项的详情数据.

(4)监听相关状态

通过监听设备的状态:休眠状态、充电状态、网络状态
结合JobScheduler来根据实际情况做网络请求. 比方说Splash闪屏广告图片, 我们可以在连接到Wifi时下载缓存到本地; 新闻类的App可以在充电, Wifi状态下做离线缓存.

(5)弱网测试&优化

除了正常的网络优化, 我们还需考虑到弱网情况下, App的表现.创建和启动Android模拟器可以设置网络速度和延迟
弱网优化, 本质上是在弱网的情况下能让用户流畅的使用我们的App. 我们要做的就是结合上述的优化项:

  • 压缩/减少数据传输量
  • 利用缓存减少网络传输
  • 针对弱网(移动网络), 不自动加载图片
  • 界面先反馈, 请求延迟提交
(6)服务器优化

包括服务器端的代码开发, 部署方式等

(4)Http与Https对访问速度(性能)的影响

HTTPS 在保护用户隐私,防止流量劫持方面发挥着非常关键的作用,但与此同时,HTTPS 也会降低用户访问速度,增加网站服务器的计算资源消耗。
影响主要来自两方面:
a、协议交互所增加的网络 RTT(round trip time)。
b、加解密相关的计算耗时。

1、网络耗时增加

HTTP 首个请求的网络耗时,用户只需要完成 TCP 三次握手建立 TCP 连接就能够直接发送 HTTP 请求获取应用层数据,此外在整个访问过程中也没有需要消耗计算资源的地方。HTTPS 的访问过程,相比 HTTP 要复杂很多,在部分场景下,使用HTTPS 访问有可能增加 7 个 RTT。

2、计算耗时增加

密钥交换时加密解密过程,需要非常消耗CPU资源的计算耗时

(二)电池使用优化

(1)分析电量使用情况工具

a.Batterystats & bugreport
b.Battery Historian

(2)主要的耗电因素

1、网络请求

2、WakeLock

Android系统本身为了优化电量的使用, 会在没有操作时进入休眠状态, 来节省电量.
可以用WakeLock来保持CPU运行, 或是防止屏幕变暗/关闭, 让手机可以在用户不操作时依然可以做一些事儿(如播放视屏);或者灭屏后执行其他操作。

3、GPS

(3)电量优化

1、优化网络请求

(见网络优化)

2、谨慎使用WakeLock

(1)WakeLock获取释放成对出现
(2)使用超时WakeLock,以防异常导致没有没有释放

// Acquires the wake lock with a timeout.
acquire(long timeout)

3、监听手机充电状态

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来监听:

<receiver android:name=".PowerConnectionReceiver">
  <intent-filter>
    <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
  </intent-filter>
</receiver>

监听电池状态, 可以让我们将一些操作放在充电或是电量足够的情况下进行, 以提升用户体验. 例如用户数据同步, Log上传等.

4、Doze and App Standby

Android 6.0提供了两个用来节省电量的技术Doze和App Standby.
(1)Doze
瞌睡. 如果设备闲置了一段较长时间, Doze技术将通过延迟后台网络活动, CPU运行等来减少电量损耗.
(2)App Standy
应用待机. 不是最近得到过用户"宠幸"的App, App Standy将延缓这个应用的后台网络活动.

5、关于定位

(1)定位中使用GPS, 请记得及时关闭

// Remove the listener you previously added
locationManager.removeUpdates(locationListener);

(2)减少更新频率
(3)根据实际情况选择GPS或网络或两者. 只使用一个会降低电量损耗.

(三)ANR详解

(1)ANR简介

1、定义

ANR全名Application Not Responding, 也就是"应用无响应".

2、产生原因

在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下两种情况下会弹出ANR对话框:
(1)5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
(2)BroadcastReceiver在10s内无法结束.
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.

3、如何避免

不要在主线程(UI线程)里面做繁重的操作.

(2)ANR分析

1、获取ANR产生的trace文件

ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:

$adb pull data/anr/traces.txt .

2、分析trace.txt

1.普通阻塞导致的ANR
 - locked <0x35fc9e33> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
2.CPU满负荷
100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait

CPU占用100%,满负荷,觉得多数是被IOwait,即IO操作占用。此时分析方法调用栈, 一般来说会发现是方法中有频繁的文件读写或是数据库读写操作放在主线程来做了.

3.内存问题(内存泄露/溢出)
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732//free内存所剩无几

3、ANR处理

1.主线程阻塞

开辟单独的子线程来处理耗时阻塞事务.

2.CPU满负荷/IO阻塞

I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.

3.内存不足

增大VM内存, 使用largeHeap属性, 排查内存泄露(内存优化)等.

(3)避免ANR方法

以预防为主,有主线程和子线程概念,认清代码的阻塞点,善用线程。
思想:不要在主线程(UI线程)里面做繁重的操作.

1、在主线程执行的操作

  • Activity的所有生命周期回调都是执行在主线程的.
  • Service默认是执行在主线程的.
  • BroadcastReceiver的onReceive回调是执行在主线程的.
  • 没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的.
  • AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的.
  • View的post(Runnable)是执行在主线程的.

2、使用子线程的方式

1.Thread方式

继承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();
2.AsyncTask方式

AsyncTask开启异步任务

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // 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);
3.HandlerThread方式

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这个子线程中了.
    }
}
4.IntentService方式

Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.

5.Loader方式

Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.

// 使用LoaderManager来初始化Loader
getLoaderManager().initLoader(0, null, this);

//如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
//如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器

// 创建一个Loader
public Loader<Cursor> 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<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);
}

(四)性能分析工具

在这里插入图片描述

1、官方工具

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的该项时间指数, 分为:

  • 绿色, 表示该View的此项性能比该View Tree中超过50%的View都要快.
  • 黄色, 表示该View的此项性能比该View Tree中超过50%的View都要慢.
  • 红色, 表示该View的此项性能是View Tree中最慢的.
    (2)作用
    用来做View层级分析, 可以分析出View Tree中的性能阻塞点, 以便对症下药, 提升布局性能.
    1.4 TraceView
    (1)说明
    在这里插入图片描述
    一个图形化的工具, 用来展示和分析方法的执行时间.
    (2)作用
    分析方法调用栈以及其执行时间, 优化方法执行.
    1.5 Memory Monitor
    (1)说明
    内存使用检测器, 可以实时检测当前Application的内存使用和释放等信息, 并以图形化界面展示.
    (2)作用
    在这里插入图片描述
    用来做内存分析, 内存泄露排查的不二之选. 可以结合heap viewer, allocation tracker来分析.可以导出hprof文件结合第三方的MAT工具分析泄露点.
    1.6 Other Monitor
    (1)说明
    Android Studio的Monitor还提供了其他三个Motinor — CPU, GPU, Network.
    (2)作用
    分别用来跟踪监测CPU,GPU和Network的使用极其变化, 可以作为网络优化, 流量优化和渲染优化等的一个指导.
    1.7 其他
    Android的开发者模式中也提供了较多的用来监测性能的选项, 可以用下:
    在这里插入图片描述
    Developer options

2、第三方工具

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中, 用来做内存问题预防最好不过了.

发布了74 篇原创文章 · 获赞 15 · 访问量 6255

猜你喜欢

转载自blog.csdn.net/qq_29966203/article/details/90473690