Android的性能优化(下)

来源:互联网 发布:淘宝店铺公告在哪修改 编辑:程序博客网 时间:2024/05/21 17:00

应用APP内存的使用,也是评价一个应用性能高低的一个重要的指标。所以不管什么样的应用,都应该把内存效率,用户体验放在首位。
由于Android应用的沙箱机制(一种安全机制),每一个应用分的的内存大小是有限度的,内存太低就会触发LMK(low memory killer)机制,先来简单的说说手机的内存吧:

  1. 寄存器
    寄存器拥有非常高的读写速度,速度最快的存储场所,因为寄存器位于处理器内部,在程序中是无法控制的。cpu访问快慢的速度依次为寄存器(register)-> 缓存(cache)->内存(memeory)->硬盘(disk).


  2. 一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配的,当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。但是对象本身是不存放在栈中的,而是存放在堆中的。


  3. 堆内存是用来存放由new创建的对象和数组的,在堆中分配的内存,由Java虚拟机的自动垃圾回收机制来回收。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。
    引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组&对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在地代码块之外,数组和对象本身占用的堆内存也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!

  4. 静态存储区域
    在固定的位置存放应用程序运行时一直存在的数据。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

  5. 常量池
    JVM虚拟机必须为每一个加载的类维护一个常量池,常量串词就是该类型所用到常量的一个有序的集合,包括直接常量(基本类型,String)和其他类型,字段,方法的符号引用。

1.获取Android系统的内存信息

Meminfo

Meminfo是系统上一个重要的内存监视工具,可以通过设置—>应用程序—>正在运行打开所有正在运行的APP的内存使用情况。

2.内存回收

Java对于C,C++这类语言最大的优势就是不用手动的管理系统资源,Java创建了垃圾收集器线程来自动进行资源的管理。但是自动回收也带来一个问题就是在何时进行垃圾回收是开发者所不能控制的,即使是调用了System.gc()方法,也只是建议系统进行GC,但是系统是否采纳你的建议那就不一定了,这样就有可能造成忘记回收的现象,这就是造成内存泄露的原因。

内存优化的实例

从Bitmap和代码的两个角度来对代码进行优化。

Bitmap的优化

我们在使用bitmap的时候经常出现的一个问题就是OOM(out of memory).在使用bitmap的时候也有一些小技巧。

  • 使用适当分辨率和大小的图片
    由于Android系统在做资源适配的时候会对不同分辨率文件夹下的图片进行缩放来适配相应的分辨率,如果图片分辨率与资源文件夹分辨率不匹配或者图片的分辨率太高,就会导致系统消耗更多的内存资源。同时,在适当的时候,应该显示合适大小的图片,例如在图片列表界面可以使用缩略图thumbnails,而在显示详细图片的时候再显示原图,或者是在对图像要求不高的地方,尽量降低图片的精度。

  • 及时回收内存
    一旦使用完Bitmap后,一定要及时使用bitmap.recycle()方法释放内存资源,但是自从Android 3.0之后,由于Bitmap被放置到堆中,其内存由GC管理,就不需要进行释放了。

  • 使用图片缓存
    通过内存缓存(LruCache)和硬盘缓存(DiskLruCache)可以更好地使用Bitmap。

代码的优化
  • 对常量使用static修饰符
  • 使用静态方法,静态方法会比普通方法提高15%左右的访问速度
  • 减少不必要的成员变量,这点在Android Lint工具上已经集成了,如果一个变量可以定义成局部变量,则会提示你不要哦定义成成员变量
  • 减少不必要的对象,使用基础类型比使用对象更加的节省资源,同时避免频繁的创建短作用域的变量
  • 尽量少使用枚举类型,少使用迭代器
  • 对Cursor,Receiver,Sensor,File等对象的时候,要注意该对象的创建,回收和注册,取消注册。
  • 使用SurfaceView来替代View进行大量,频繁的绘图操作
  • 多使用视图缓存技术,而不是每次都执行inflate()方法解析视图

3.Lint工具

Android Lint工具是Android Studio中集成的一个Android代码提示工具,它可以对我们的布局,代码提供非常强大的帮助,它可以检查出:xml文件中是否存在hardcode硬编码、unused resources没有使用到的资源、probable bug可能的bug,可能出现的冗余布局等等。
这里写图片描述
这里写图片描述

4.Android Studio的Memory Monitor工具

Memory Monitor是Android Studio自带的一个内存监视工具,可以很好的帮我们进行内存实时的分析,Memory Monitor里面包括了内存分析,cpu的占用比,gpu的绘制速度,以及网略请求的下载速度等分析工具:
这里写图片描述

图中较浅蓝色代表free的内存,深色的部分代表的是使用的内存从内存变换的走势图,可以判断内存的使用情况,例如当内存持续增高的时候可能发生内存泄露,当内存突然减少的时候就有可能被GC回收了。

5.使用TraceView工具优化APP性能

TraceView是Android下的一个日志分析下工具。

5.1生成TraceView日志的两种方法

生成TraceView日志的两种方法,一种是利用Debug类帮助我们呢生成日志文件,另一种是利用Android Device Monitor工具辅助生成日志文件。

  • 通过代码生成精确范围的生成TraceView的日志
    使用Debug类的startMethodTracing()方法开启日志的记录,通过Debug类的stopMethodTracing()方法结束日志的监听。将这两个方法对某一代码块进行监听,就可以记录该代码块中的日志情况。比如说在onCreate()方法中调用startMethodTracing()方法,在onDestory()方法中调用stopMethodTracing()方法结束日志的打印。TraceView的日志最终将会保存到SD卡中。也可以调用startMethodTracing(String traceName) 设置trace文件的文件名,最后使用adb pull /sdcard/test.trace /tmp 命令将trace文件复制到你的电脑中,用DDMS工具就可以打开了。
    写入SD卡中,注意要添加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  • 通过Android Device Monitor生成TraceView日志
    在Android Studio的Android Device Monitor工具,选择要调式的进程,点击工具栏中的“start method profiling”按钮。
    这里写图片描述

这里写图片描述

6.分析TraceView日志

由于自己的Android手机没有Root,所以此处就借用Genymotion中系统的程序(calendar)进行分析下。
这里写图片描述
这里写图片描述

时间轴区域内显示了不同线程在不同的时间段内的执行情况,在时间轴中,每一行都代表了一个独立的线程。在时间轴中我们可以看到不同的颜色,每种颜色代表不同的函数和步骤,同一颜色表示的区间越大,表示这个方法运行时间越长。

Profile区域显示的是所有的调用方法,前面的是编号,展开后可以看到有Parent和Children子项,点击Parents下的方法名,直接跳转到调用当前的方法处。Children相反。
这里写图片描述

Profile区域主要显示的信息:

Inclusive time - 函数本身运行花费时间 + 函数调用其他函数时间

Exclusive time - 函数本身运行花费时间

Calls + RecurCall/Total 调用 + 重复调用次数 / 函数总调用次数

Cpu Time/Call 总的Cpu时间与总的调用次数之比

每一个时间都包含两列,一个是实际的时间,另一个是百分比,分析的时候对占用时间长的方法进行重点的分析,如果说占用的时间长且调用的次数少,那么就是重点怀疑的对象了。

0 0