前言

之前我所担任的项目运用的是购买的制品安卓设备,所以一向没有什么问题。

不久之前,老板决定不再购买制品设备,而是自己规划制作安卓硬件设备。

可是替换硬件之后,运转同一个 APP 的同一个版别会呈现卡顿的现象。

并且开机时刻越长该现象越明显,当开机时刻达到必定时刻后,甚至卡顿到完全没法运用。

而且卡顿时不仅是咱们的 APP 卡顿,而是整个体系都在卡顿,这明显是散热有问题。

可是老板可不会听咱们的所谓“明显”,凡事都需求拿出依据。

所以就有了这篇文章的内容。

基础常识

监控手机的运转性能运用多种方法都能够完成。

例如,能够运用 Android Studio 的 Profile 东西直接录制。

也能够下载其他第三方 APP 来监控记载。

可是,由于咱们这儿的背景是需求检查形成体系卡顿的原因,所以可想而知,到后期时整个体系运转有多吃力,此刻还挂着第三方 APP 的话,大概率会被强杀进程,或者干脆直接卡死(别问我怎样知道的)。

而运用 Profile 的话,问题在于本身这个东西在数据量大时就非常卡,而咱们需求做的监控是少则需求录制一天时刻的,到时估计录制出来的文件都打不开了(别问我怎样知道的x2)。

所以,咱们这儿挑选直接运用 ADB 运转 shell 指令获取需求的数据,然后保存下来。

因而,在开端之前,咱们需求了解一些基础常识,信任 ADB 的运用作为安卓开发的大伙都不会生疏吧。

咱们这儿就简略介绍几个接下来可能会用到的 ADB 常用常识。

获取当时衔接的设备:

adb devices

回来:

List of devices attached
78cb57bd        device

其间前面的 “78cb57bd” 为 transport id ,在咱们同时衔接多个设备时能够运用 -t transport id 指定发送指令的设备。

在设备上履行 shell 指令

adb shell command

其间的 command 即为需求履行的 shell 指令,例如: 在设备上履行 ls 指令:

adb shell ls

可是我比较懒,不想每次都打这么长的指令,那咱们就能够经过:

adb sehll

进入到设备的终端 shell 中,此刻履行 shell 指令就不需求加上 adb shell 前缀了。

留意:接下来的指令其实都不是 adb 指令了,而是 shell 指令。

获取当时设备已装置的运用包名

pm list packages

获取当时运转的进程列表

ps -e -o PID,NAME

这个主要是用来获取运用的 pid 。

其间 ps 表明获取当时运转的进程,-e 表明输出一切进程,-o 表明指定输出内容,这儿咱们指定只输出 pid 和名称(即一般运用的包名)。

需求的指令

获取内存信息

能够运用指令 dumpsys meminfo 输出完好的内存信息:

cas:/ $ dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 5209932538 Realtime: 7380312852
Total RSS by process:
    643,928K: system (pid 1633)
    343,276K: com.android.systemui (pid 3552)
    325,780K: com.tencent.mobileqq (pid 1402 / activities)
    298,708K: com.tencent.mm (pid 4045 / activities)
    269,648K: com.douban.frodo (pid 6511)
    245,488K: com.android.phone (pid 3548)
    242,148K: com.miui.home (pid 23433 / activities)
    231,992K: com.miui.securitycenter.remote (pid 32725)
    221,004K: tv.danmaku.bili (pid 5776)
    199,776K: com.sohu.inputmethod.sogou (pid 15661)
    168,496K: com.tencent.wework (pid 26035 / activities)
    141,364K: com.miui.aod (pid 31856)
    134,662K: surfaceflinger (pid 1253)
    130,180K: com.mi.health:device (pid 5775)
    130,156K: com.tencent.mm:push (pid 22102)
    121,716K: com.google.android.gms.persistent (pid 13831)
    121,482K: com.equationl.starryskywallpaper (pid 26623)
    114,552K: com.android.settings (pid 4751)
    109,164K: com.douban.frodo:pushservice (pid 6836)
    104,212K: com.tencent.mobileqq:MSF (pid 3095)
    104,160K: com.google.android.gms (pid 13826)
    103,244K: tv.danmaku.bili:download (pid 26771)
    102,764K: tv.danmaku.bili:pushservice (pid 26747)
     96,304K: com.android.vending (pid 32161)
     94,752K: com.miui.personalassistant (pid 16045)
     94,608K: com.tencent.wework:wxa_container0 (pid 26374)
     93,236K: com.xiaomi.market (pid 16530)
     91,472K: com.android.bluetooth (pid 25257)
     90,828K: com.xingin.xhs:longlink (pid 2909)
     89,672K: com.android.calendar (pid 22772 / activities)
     88,108K: com.miui.voiceassist (pid 11476)
     85,172K: system:ui (pid 26808)
     82,280K: com.miui.cloudservice (pid 6997)
     80,744K: com.miui.phrase (pid 6978)
     79,200K: com.miui.powerkeeper (pid 8331)
     78,188K: com.miui.miwallpaper (pid 23645)
     77,948K: com.miui.analytics (pid 8904)
     77,816K: com.google.android.gms.unstable (pid 11157)
     75,996K: com.miui.touchassistant:float (pid 17509)
     73,892K: com.xiaomi.misettings (pid 5333)
     73,524K: tv.danmaku.bili:ijkservice (pid 29513)
     72,384K: com.miui.systemAdSolution (pid 10661)
     72,016K: com.android.mms (pid 27473)
     71,964K: com.xiaomi.aiasst.service (pid 28497)
     69,500K: com.google.android.wearable.app.cn:background (pid 21870)
     69,172K: com.xiaomi.metoknlp (pid 31268)
     67,188K: com.google.android.webview:webview_service (pid 6603)
     67,048K: com.xiaomi.xmsf (pid 30709)
     66,048K: com.miui.yellowpage (pid 3714)
     65,820K: com.lbe.security.miui (pid 23573)
     65,684K: com.miui.misound (pid 20012)
     65,512K: com.miui.mishare.connectivity (pid 3230)
     64,484K: com.google.android.gms.persistent (pid 9804)
     64,480K: com.xiaomi.bluetooth (pid 14657)
     63,724K: com.miui.screenrecorder (pid 3447)
     63,148K: com.xiaomi.account (pid 16742)
     62,560K: com.xiaomi.gnss.polaris:remote (pid 3996)
     62,360K: com.miui.daemon (pid 5664)
//.....................

可是输出的内容太多,反而不好使,所以咱们需求稍微过滤一下需求的内容。

例如获取某个运用的内存信息能够运用 dumpsys meminfo pkg | pid ,其间的 pkg | pid 能够运用运用包名也能够运用 pid,例如获取微信的内存占用:

cas:/ $ dumpsys meminfo com.tencent.mm
Applications Memory Usage (in Kilobytes):
Uptime: 5210015496 Realtime: 7380395810
** MEMINFO in pid 4045 [com.tencent.mm] **
                   Pss  Private  Private  SwapPss      Rss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty    Total     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------   ------
  Native Heap    28441    28396        0   148021    29284   202460   184218    18241
  Dalvik Heap    66381    66340        4     5605    67456    75619    67427     8192
 Dalvik Other    11010    10296        4     1160    12436                           
        Stack     3068     3068        0     8488     3076                           
       Ashmem       65       48        0        0      848                           
      Gfx dev    10672    10672        0        0    10672                           
    Other dev       18        0       16        0      372                           
     .so mmap     9843     2440     5972    12073    24296                           
    .jar mmap     1006        0        8        0    35676                           
    .apk mmap    17376        0    11736        0    23880                           
    .ttf mmap     1615        0        0        0    19096                           
    .dex mmap     6768       32     6632       64     7488                           
    .oat mmap      225        0        0        0    12964                           
    .art mmap    20532    20020      168     6204    30516                           
   Other mmap      596       24      524        0     1072                           
    GL mtrack      384      384        0        0      384                           
      Unknown     6007     6004        0    40216     6272                           
        TOTAL   405838   147724    25064   221831   285788   278079   251645    26433
 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:    86528                          97972
         Native Heap:    28396                          29284
                Code:    26864                         124880
               Stack:     3068                           3076
            Graphics:    11056                          11056
       Private Other:    16876
              System:   233050
             Unknown:                                   19520
           TOTAL PSS:   405838            TOTAL RSS:   285788       TOTAL SWAP PSS:   221831
 Objects
               Views:     2955         ViewRootImpl:        1
         AppContexts:       12           Activities:        1
              Assets:       33        AssetManagers:        0
       Local Binders:      298        Proxy Binders:      621
       Parcel memory:      410         Parcel count:      326
    Death Recipients:      496      OpenSSL Sockets:        0
            WebViews:        0
 SQL
         MEMORY_USED:      642
  PAGECACHE_OVERFLOW:      226          MALLOC_SIZE:       46
 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       96             55       66/85/25  /data/user/0/com.tencent.mm/no_backup/androidx.work.workdb
         4        8                         0/0/0    (attached) temp
         4       96             40         3/16/4  /data/user/0/com.tencent.mm/no_backup/androidx.work.workdb (2)
         4       92             46      3703/27/4  /data/user/0/com.tencent.mm/databases/Scheduler.db
         4      108            124       27/31/16  /data/user/0/com.tencent.mm/databases/google_app_measurement.db
cas:/ $ 

能够看到,输出内容少了许多,可是还是不可精简 ,咱们需求的仅仅这个运用占用的总内存而已。

而在上述输出中,咱们能够看到几种不同的内存占用,他们的含义分别如下:

  • VSS – Virtual Set Size 虚拟耗用内存(包括同享库占用的内存)
  • RSS – Resident Set Size 实践运用物理内存(包括同享库占用的内存)
  • PSS – Proportional Set Size 实践运用的物理内存(份额分配同享库占用的内存)
  • USS – Unique Set Size 进程独自占用的物理内存(不包括同享库占用的内存)

咱们一般需求检查的是 PSS 值,因而咱们能够将上述指令加上过滤操作:

dumpsys meminfo com.tencent.mm | grep "TOTAL PSS"

回来如下:

cas:/ $ dumpsys meminfo com.tencent.mm | grep "TOTAL PSS"
           TOTAL PSS:   404910            TOTAL RSS:   287560       TOTAL SWAP PSS:   219203
cas:/ $ 

其间在指令后边添加的 grep 表明过滤内容,后边跟着的内容即表明需求查找的内容,支撑正则,也支撑字符串匹配,在这儿的意思是仅过滤出包括 “TOTAL PSS” 的这一行输出。

而两个指令之间的 | 符号是管道衔接符,表明衔接两个指令。

终究,还能够经过以下两种不同的方法获取到当时手机的总内存占用:

dumpsys meminfo | grep -n "RAM" procrank | grep "RAM"

从指令不难看出,它们分别是从不同的指令中过滤出咱们需求的总内存信息,至于它们原本的输出是什么,感兴趣的能够自己把参数和过滤去掉履行一下看看。

后边的指令咱们就不一一解释这个过滤操作了。

终究的终究,附加几个检查当时体系的 APP 可用最大内存装备的方法:

  • 单个运用的最大内存约束
    getprop | grep heapgrowthlimit
  • 运用发动后分配的初始内存
    getprop|grep dalvik.vm.heapstartsize
  • 单个java虚拟机的最大内存约束
    getprop|grep dalvik.vm.heapsize

CPU 当时占用信息

获取 CPU 的占用信息仍旧是有两种方法。

方法一:

top -n 5 | grep "com.tencent.mm"

其间 -n 5 表明指定履行 5 次,如果不指定次数则会实时刷新当时的 CPU 占用情况。

回来数据:

cas:/ $ top -n 5 | grep "com.tencent.mm"
 7491 shell        20   0 2.0G 3.6M 2.9M S  0.0   0.0   0:00.01 grep com.tencent.mm
 4045 u0_a276      20   0 129G  91M 2.1M S  0.6   1.1  10:38.22 com.tencent.mm

需求留意的是,第一行数据不是微信的占用数据啊,这个是咱们方才履行的这个 shell 的占用信息。

第二行才是微信的信息,其间的 “S 0.6” 即微信的占用率,这儿表明为 0.6% 。

第二种方法:

dumpsys cpuinfo | grep "com.tencent.mm"

回来数据:

cas:/ $ dumpsys cpuinfo | grep "com.tencent.mm"
  0.6% 4045/com.tencent.mm: 0.4% user + 0.1% kernel / faults: 122 minor 18 major
  0.2% 22102/com.tencent.mm:push: 0.1% user + 0% kernel / faults: 47 minor 1 major
cas:/ $ 

第一列数据即微信的总占用率,后边则是它的详细数据。

CPU 温度

咱们能够经过读取 /sys/class/thermal/thermal_zone*/temp 文件获取到各个传感器的温度值,即:

cat /sys/class/thermal/thermal_zone*/temp

回来数据:

cas:/ $ cat /sys/class/thermal/thermal_zone*/temp
35400
36200
35800
35000
36200
35800
35800
36200
35200
35200
35600
36000
35800
35600
36000
35600
34500
36400
36400
37000
36600
36000
36600
36200
36200
36200
37000
35400
35400
35400
35800
35000
36200
36200
36600
35800
35600
35600
36000
35600
35600
36000
34900
33000
35000
37400
// ...............

能够看到,回来了许多数据,那么哪个才是咱们需求的数据呢?

咱们能够经过读取 /sys/class/thermal/thermal_zone*/type 文件获取到每一行对应的是什么的传感器,即:

cat /sys/class/thermal/thermal_zone*/type

回来数据:

1|cas:/ $ cat /sys/class/thermal/thermal_zone*/type
aoss0-usr
cpu-0-0-usr
cpu-1-3-usr
cpu-1-4-usr
cpu-1-5-usr
cpu-1-6-usr
cpu-1-7-usr
gpuss-0-usr
aoss-1-usr
cwlan-usr
video-usr
ddr-usr
cpu-0-1-usr
q6-hvx-usr
camera-usr
cmpss-usr
npu-usr
gpuss-1-usr
gpuss-max-step
apc-0-max-step
apc-1-max-step
pop-mem-step
cpu-0-0-step
cpu-0-2-usr
cpu-0-1-step
// ........

如果不想要这么多数据,仅仅想要某个方位的数据,那么也不需求用过滤,直接把上面文件路径中的 * 换为相应的序号(即行号)即可,例如获取 cpu-0-0-usr 的温度:

cat /sys/class/thermal/thermal_zone1/temp

电池温度

如果你觉得从上面的一堆数据中找出电池的温度数据太麻烦了,那么你能够运用别的一个单独的指令获取到电池的温度数据:

dumpsys battery | grep temperature

CPU 当时频率

获取 CPU 的当时频率依然是有两种方法。

方法一:

cat /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq

其间的 cpuX 为 cpu 的中心序号,例如想获取序号为 0 的中心的运转频率能够写成:

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

回来数据:

cas:/ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
1804800

需求留意的是这种方法回来的不是硬件当时实践的运转频率,而是调度程序发送给硬件,让硬件应该以这个频率来运转,一般来说,此刻硬件确实是以这个频率运转的,可是有时分硬件可能会有点“固执”,就是不按这个频率运转,此刻咱们就需求别的一个指令:

cat /sys/devices/system/cpu/cpuX/cpufreq/cpuinfo_cur_freq

其间的 cpuX 仍旧为 cpu 的中心序号。

现在回来的就是当时硬件实践的运转频率了,可是很不幸,在高版别安卓上现已不允许读取这个文件了,想要读取的话有必要拥有 root 权限。

编写监控脚本

现在,咱们现已知道了获取所需数据的 shell 指令了,是时分来编写一个脚本实时获取需求的数据了:

echo "watcher running..."
log_path="/sdcard/watcher.log"
echo "start watch state at $(date)n" >> $log_path
while true
do
  sleep 1
  gpu_temp=$(cat /sys/class/thermal/thermal_zone1/temp)
  msg="$(date), GPU Temp: $gpu_tempn"
  echo "$msg"
  echo "$msg" >> $log_path
  cpu_temp=$(cat /sys/class/thermal/thermal_zone0/temp)
  cpu_0=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq)
  cpu_1=$(cat /sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_cur_freq)
  cpu_2=$(cat /sys/devices/system/cpu/cpu2/cpufreq/cpuinfo_cur_freq)
  cpu_3=$(cat /sys/devices/system/cpu/cpu3/cpufreq/cpuinfo_cur_freq)
  msg="CPU Temp: $cpu_temp, CPU0: $cpu_0, CPU1: $cpu_1, CPU2: $cpu_2, CPU3: $cpu_3, n"
  echo "$msg"
  echo "$msg" >> $log_path
  cpu_usage_info=$(top -n 1 | grep "com.xxxx.yyyy")
  msg="CPU Usage: $cpu_usage_info"
  echo "$msg"
  echo "$msg" >> $log_path

  # 这个速度非常慢,如果对记载速度有要求的话,最好不要加这个
  mem_info_total=$(procrank | grep "RAM")
  mem_info_current=$(dumpsys meminfo com.xxxx.yyyy | grep -n "TOTAL PSS")
  msg="Mem Info, Total: $mem_info_total, Current: $mem_info_currentn"
  echo "$msg"
  echo "$msg" >> $log_path
done

上面这个脚本非常的简略,就是开启一个 while 循环,然后每隔 1s 读取一次各项数据,并将其写入 /sdcard/watcher.log 文件中。

咱们将以上脚本内容保存为恣意文件,例如: watcher.sh,然后放到设备的恣意方位,例如 /sdcard/watcher.sh

在 shell 中履行 sh /sdcard/watcher.sh 即可,运转后咱们只需求正常运用咱们的设备,这个脚本会在后台静静的帮咱们把数据都记载下来的。

PS:上面这个脚本因为仅仅自己运用,所以各项数据挑选做的很粗糙,筛了一堆没用的数据进来,各位大佬运用的时分最好根据自己需求重新挑选一下数据。

结束

在开着这个脚本跑了一天之后,终究发现,随着设备的开机,温度一向在上升,上升到某个值后好像撞到了温度墙,就开端在这个温度范围内波动,同时 CPU 的各个中心运转频率开端大幅的下降。除此之外,其他各项数值均未见异常。

换句话说,这证明了形成卡顿的原因确实是因为散热不可导致 CPU 降频,终究导致体系卡顿。

在这铁一般的依据面前,老板总算“放过了”我,转而去找硬件工程师去了。哈哈哈哈。