0%

为什么需要优化

Android中内存优化是一个比较重要的话题,因为内存会直接影响到程序的运行性能,在开发中经常会出现一些内存相关的问题,包括内存泄露,内存抖动,占用过多内存等,因此优化的目的,就是让减少这几类问题的出现,或者彻底解决,让程序运行起来更加流畅,性能更高效。

常用检测工具

  • LeakCanary

    用于检测内存泄露,如果发生内存泄露,会给出泄露对象的引用链,其内部是通过WeakReferences的回收队列实现内存泄露检测的。

  • Android Studio的Profiler工具

    可查看对象占用内存情况,只能查看debuggable应用。

  • adb命令

    执行命令adb shell dumpsys meminfo,可以查看所有应用的内存信息。常用的参数有–package,用于指定要查看的包名的所有进程的内存信息,比如我们要查看微信的内存信息,执行下面的命令:

    adb shell dumpsys meminfo --package com.tencent.mm

    这里就会微信所有进程的内存信息,如果我们只需要看主进程的信息,可以直接通过pid指定,所以先执行命令ps获取到pid:

    adb shell ps -A | grep com.tencent.mm

    打印结果如下:

    u0_a170       3525   633 2068856  21644 0                   0 S com.tencent.mm:appbrand1
    u0_a170      19179   633 2577364 112404 0                   0 S com.tencent.mm
    u0_a170      19404   633 2240664  11740 0                   0 S com.tencent.mm:appbrand0
    u0_a170      26971   633 1965968   5312 0                   0 S com.tencent.mm:exdevice
    u0_a170      28292   633 2003436   5296 0                   0 S com.tencent.mm:push

    看到主进程的pid为19179,然后执行命令:

    adb shell dumpsys meminfo 19179

    打印结果如下:

    Applications Memory Usage (in Kilobytes):
    Uptime: 138531332 Realtime: 241333585
    
    ** MEMINFO in pid 19179 [com.tencent.mm] **
                       Pss  Private  Private  SwapPss     Heap     Heap     Heap
                     Total    Dirty    Clean    Dirty     Size    Alloc     Free
                    ------   ------   ------   ------   ------   ------   ------
      Native Heap    40613    40564        0    72101   164480   132995    31484
      Dalvik Heap    19065    19016        0     8873    52069    27493    24576
     Dalvik Other    11200    11196        0      893                           
            Stack      100      100        0       16                           
           Ashmem        4        4        0        0                           
          Gfx dev     6432     6432        0        0                           
        Other dev       28        0       28        0                           
         .so mmap     6138      440     4328     3370                           
        .jar mmap       28       28        0      336                           
        .apk mmap     1157       88      672      160                           
        .ttf mmap       60        0        0        0                           
        .dex mmap    22720        0    19372       28                           
        .oat mmap     2520        0        4        0                           
        .art mmap    10700     9644      260     3067                           
       Other mmap      336        0      324        4                           
       EGL mtrack     9248     9248        0        0                           
        GL mtrack     5436     5436        0        0                           
          Unknown     2666     2664        0     4830                           
            TOTAL   232129   104860    24988    93678   216549   160488    56060
    
     App Summary
                           Pss(KB)
                            ------
               Java Heap:    28920
             Native Heap:    40564
                    Code:    24932
                   Stack:      100
                Graphics:    21116
           Private Other:    14216
                  System:   102281
    
                   TOTAL:   232129       TOTAL SWAP PSS:    93678
    
     Objects
                   Views:     1388         ViewRootImpl:        1
             AppContexts:        7           Activities:        1
                  Assets:        7        AssetManagers:        0
           Local Binders:      157        Proxy Binders:       98
           Parcel memory:       82         Parcel count:      327
        Death Recipients:        8      OpenSSL Sockets:        0
                WebViews:        0
    
     SQL
             MEMORY_USED:      528
      PAGECACHE_OVERFLOW:       64          MALLOC_SIZE:      117
    
     DATABASES
          pgsz     dbsz   Lookaside(b)          cache  Dbname
             4      436             97       25/40/10  /data/user/0/com.tencent.mm/databases/beacon_tbs_db
             4      108            109       27/42/16  /data/user/0/com.tencent.mm/databases/google_app_measurement.db
             4       36             99         4/34/6  /data/user/0/com.tencent.mm/databases/tes_db
    
     Asset Allocations
        : 132K

    对于第一部分,一般只需要关注前两列就行,Pss Total表示该进程占用内存大小,包括了按比例计算进程间共享内存,而Private Dirty就只是该进程占用内存大小。然后对应到具体的行,Native Heap为本地堆占用内存大小,Dalvik Heap为Java堆占用内存大小,Dalvik Other为JIT和垃圾回收记录占用内存大小,.so mmap为映射的so代码占用的内存大小,.dex mmap为映射的dex代码占用的内存大小。

    对于第三部分,表示在当前进程中,某些重要类型的实例对象数量,比如ViewRootImpl就是窗口数量,AppContexts就是Context数量,而Activities为Activity的数量。

Read more »

为什么需要优化

Android布局可能存在的主要问题有两个:过度绘制和卡顿,过度绘制会浪费资源,卡顿会影响用户体验,甚至导致ANR,因此需要进行优化,优化都是围绕着这两个问题展开的,最终目的就是减少过度绘制,减少卡顿。

常见检测工具

有时通过直觉无法明显发现过度绘制和卡顿,或者无法准确量化其严重程度,因此需要借助检测工具来帮助我们排查问题,常用的工具主要有两个:开发者选项中的调试GPU过度绘制和GPU呈现模式分析。

Read more »

Protobuf是什么

全称为Protocol Buffers,Google推出的序列化框架,用于将自定义数据结构序列化成字节流,和将字节流反序列化为数据结构,该框架不依赖开发语言,也不依赖运行平台,扩展性好,目前支持的语言比较多,包括Java,C++,Python,Ruby等。

使用Protobuf

在这里使用Windows和Java进行实例演示:

Read more »

散列算法

散列算法不属于加密算法,目前经常使用的有两种:

1. MD5

对任意长度的数据计算生成128位固定长度的MD5值,主要的特定:

  • 生成的MD5值一定是128位,不管输入数据多大

  • 原始数据只要有修改,MD5值就会发生很大的改变

  • 容易计算,也就是算法不会很耗时

  • 很少会发生碰撞

MD5的算法过程主要为:填充 –> 分组进行位运算 –> 得到MD5值

Read more »

相关概念

  • 屏幕分辨率

    经常说的1080*1920,表示宽高的像素个数。

  • 屏幕尺寸

    手机对角线的尺寸,单位是英寸,1英寸=2.54cm。

  • dpi

    每英寸的像素个数,比如10801920的手机,5.5英寸,那么dpi为sqrt(10801080+1920*1920)/5.5=354

    而这里,Android定义了一系列的密度类型,比如mdpi为160,hdpi为240,xhdpi为320,xxhdpi为480。

  • dp

    密度无关像素,1dp的像素数跟dpi有关,1dp=dpi/160。

  • sp

    独立比例像素,跟dp类似,但它还跟字体大小相关联。

Read more »

普通文件读写过程

读文件:会先把文件数据拷贝到内核空间中的页缓存中,然后再把页缓存的数据拷贝到用户空间中,这里需要两次数据拷贝。

写文件:先把用户空间的数据拷贝到内核空间,内核空间再写回磁盘,也是两次数据拷贝。

mmap实现过程

  1. 在当前进程的虚拟地址空间中寻找一段满足要求的虚拟地址,然后创建vm_area_struct结构,对其进行初始化,最后将其插入到进程的虚拟地址区域链表(或者树)中。

  2. 内核根据fd找到文件磁盘物理地址,建立页表实现文件地址和虚拟地址区域的一一对应。

  3. 进程对映射的虚拟地址空间发起访问,引发缺页中断,系统会把所缺的页装入主存中。

Read more »

V1签名过程

随便找个apk解压,会看到有个META-INF文件夹,这个就是跟apk的签名有关的,里面会有三个文件:MANIFEST.MF,ANDROID.SF,ANDROID.RSA。

  • MANIFEST.MF

    这个保存了apk中所有文件的消息摘要值,遍历apk所有的文件,对于每一个文件,都用SHA1(或者SHA256)计算其摘要,然后再做Base64编码,得到的值和文件路径组成key-value,写入到MANIFEST.MF文件中。示例如下:

    Manifest-Version: 1.0
    
    Name: AndroidManifest.xml
    SHA1-Digest: bVaU03v/s2D1ZnfNly3inexGx8E=
    
    Name: LICENSE-junit.txt
    SHA1-Digest: xa0kHcW0TK5p5IxSU7ej/t5BXqE=
    
    Name: LICENSE.txt
    SHA1-Digest: dTF36wNXk3NZRvp51lzE8FW7KYQ=
    
    Name: R/a/gu.xml
    SHA1-Digest: EImnskGmHG13zzjLQsBz7AaLVGU=
    
    Name: R/a/hf.xml
    SHA1-Digest: 1UI3hZQyzdEjUG/usTIhx245zDM=
    ......
  • CERT.SF

    这个是对MANIFEST.MF中的所有条目,再次进行SHA1+Base64处理,最终的结果同样组成key-value,写入到CERT.SF文件中。示例如下:

    Signature-Version: 1.0
    Created-By: 1.0 (Android)
    SHA1-Digest-Manifest: ktRWgoJhhmR9UKW2bkvJv9sWbwg=
    X-Android-APK-Signed: 2
    
    Name: AndroidManifest.xml
    SHA1-Digest: vPXU1jU9CV5ThOTQ1dhPspFhSDY=
    
    Name: LICENSE-junit.txt
    SHA1-Digest: i/XNa154J6oo2MWLxVtyjXAag2Q=
    
    Name: LICENSE.txt
    SHA1-Digest: dtxUfw7RZZWDxsyMx++B4WEugqM=
    
    Name: R/a/gu.xml
    SHA1-Digest: QgyDHCXLVsWQFsZVYbvZnimS81w=
    
    Name: R/a/hf.xml
    SHA1-Digest: N8tGKy5jd9e3TmOtLTmfwtUW1LM=
    ......
  • CERT.RSA

    首先这里需要一个证书,其实就是平时开发用到签名文件,然后利用这个证书里面的私钥对CERT.SF计算得到签名,然后把这个签名和证书写入到CERT.RSA中,注意写入的证书包含了公钥信息,私钥是不会写进去的。这里用到的证书是自签名的,也就是开发者自己生成,不用要求必须是CA机构发布的。

Read more »

1. 初始化

WebView webView = new WebView(this);

这里需要区分首次和非首次,首次创建WebView需要几百ms,而非首次创建为几ms到几十ms。

可以看到,这里初始化是比较耗时的,而我们有时直接把它放到UI线程,或者写在xml中是有问题,会导致打开页面卡死甚至ANR,应该是放到子线程中初始化结束后才添加到View树中。

Read more »