Android性能相关--经验篇

来源:互联网 发布:微信开发 未备案域名 编辑:程序博客网 时间:2024/06/14 04:14

基础知识

底层触发回收机制时机:

  • GC_MALLOC内存分配失败时触发
  • GC_CONCURRENT分配的对象大小超过384K时触发
  • GC_EXPLICIT对垃圾手机的显示调用(System.gc)
  • GC_EXTERNAL_ALLOC外部内存分配失败时触发

常见性能问题

UI卡顿:60fps=1000ms / 16ms

  • ui线程有微耗时操作
  • overdraw(层叠太多,Layout布局重叠或View在draw的时候重叠)
  • 同一时间执行动画次数太多
  • view频繁触发measure、layout导致measure、layout耗时过多以及view的重新渲染
  • 内存频繁触发GC,导致暂时阻塞渲染操作
  • 冗余资源及逻辑等导致加载和执行缓慢

ANR:1.主线程5s内没有处理完输入事件 2.主线程10s没处理完BroadCastReceiver.onReceive 3.主线程在Service各生命周期函数时20s没处理完

  • 其他进程(包括系统)占用CPU,导致App分配不到足够的CPU时间片。CPU很高,说明当前设备很忙,CPU资源被抢占导致ANR–>Log中搜ActivityManager标签
  • 主线程有耗时操作
    ○ IOwait很高,很可能主线程执行耗时IO操作
    ○ 网络访问
    ○ 大量的数据读写
    ○ 数据库操作
    ○ 硬件操作(camera等)
    ○ 其他线程持有锁,导致主线程等待超时
  • 其他,主线程被BLOCK

OOM:试图申请的内存+已分配的内存>虚拟机允许的最大内存

  • ArrayMap/SparseArray轻量级的数据结构
  • 避免使用Enum 枚举
  • 图片优化
  • String的拼接用StringBuilder
  • onDraw里不创建对象
  • 避免内存泄漏(应用分配的堆内存16M-48M)
    • 静态变量持有大量数据引用
    • 资源关闭、注册注销
    • 非静态内部类
    • ApplicationContext代替ActivityContext
    • Handler等异步类的泄露
  • 内存对象的重复利用
    • 对象池(自建或利用系统,最近使用最多算法)
    • 利用系统自带资源(字符串、颜色、图片、动画、样式、简单布局)
    • StringBuilder、StringBuffer

内存泄漏:无用对象持续占用内存或得不到及时释放

  • Activity的内存泄漏:内部类(Handler和Thread)、Context(尽量使用Application的Context,Dialog必须使用Activity的Context)
  • 临时资源主动回收
  • 监听器的回收(register、unregister、add listener、remove listener)
  • WebView本身泄漏问题(可开一个进程加载WebView,不用时直接销毁整个进程)
  • Cursor对象的关闭
  • 单例泄漏:单例的生命周期跟app一样长,如果单例持有activity的context等会导致其无法释放【可持有context.getApplicationContext()】
  • 匿名内部类/非静态内部类(尤其是放在Activity中的):静态内部类默认持有外部类的引用,若内部类有静态变量则其生命周期也是跟app一样长,导致外部类(尤其为Activity)无法释放【可将内部类置为静态类则不会持有外部类引用】
  • Handler:handler为内部类会持有外部类(尤其Activity)的引用,而activity结束时若handler的消息还没处理完则导致内存泄漏【handler置为静态类,且内部若持有外部对象则置为弱引用WeakReference】
  • AsyncTask:内存泄漏情况跟Handler一样【外部类退出时调用AsyncTask.cannel()结束掉它】
  • 资源未关闭:广播的注册于注销等

内存泄漏检测

  • 优先处理常见泄漏Analyze-Inspect Code:常见的有内部类Handler/Thread、IO操作Cursor/FileInputStream、View中使用TypedArray、四大组件的Context(Dialog、startActivity、Layout.Inflation必须用Activity;其他都可以用Application的)
  • 开源检测项目Leakcanary
  • adb shell;dumpsys meminfo com.hsb.mydemo -d;进入页面前调用命令,查看view等数量–>进入页面各种操作–>退出页面手动触发GC–>调用命令查看view等数量是否一致
  • MAT

App闪退常见原因:NullPointer、OOM、数组越界

Force Close常见原因:Error、OOM(内存溢出)、StackOverFlowError、RuntimeException空指针异常


优化建议

代码优化建议

  • 数据结构的选择:SparseArray代替HashMap,需注意:1、SparseArray不是线程安全的2、插入按key排序3、删除做了优化,不会立即删除而是设置标志位,后面尝试重用
  • Handler和内部类的正确用法
private static Class InnerHandler extends Handler{    private final WeakReference<HandlerActivity> mActivity;    public InnerHandler(HandlerActivity activity){        mActivity = activity;    }    @Override    public void handleMessage(){        HandlerActivity activity = mActivity.get();        if(activity != null){...}    }}
  • 正确使用Context

应用中的Context个数=Activity个数+Service个数+1个ApplicationContext
Application是一个单例,可以context.getApplicationContext()来获取
Activity、Service都是ContextWrapper子类,可以通过getBaseContext来获取各自的Context
BroadcastReceiver有系统通过onReceive传入的一个Context,该Context经过系统的功能裁剪,不可调用registerReceiver和bindService
ContentProvider可以通过getContext获取一个系统传入的Context。如果ContentProvider和调用者在同一个进程中则Context返回的是全局唯一的Context实例;如果不在同一个进程,则Context是ContentProvider所在进程的Context实例

Context的使用问题在于:在单例中如果传入的Context刚好是Activity/Service的则Activity/Service销毁的时候因为单例还存在则导致Activity/Service无法回收
正确使用方式

public class SingleInstance{    private Context mContext;    private static SingleInstance sInstance;    private SingleInstance(Context context){        mContext = context;    }    public static SingleInsatance getInstance(Context context){        if(sInstance == null){            sInstance = new SingleInstance(context.getApplicationContext());        }        return sInstance;    }}
  • 对常量使用static修饰
  • 使用过静态方法比普通方法提高15%访问速度
  • 减少成员变量的定义(尽量缩小变量的作用域)
  • 尽量减少new对象
  • 尽量少使用枚举、迭代器
  • 对Cursor、Receiver、Sensor、File等对象注意他们的创建-回收-注册-解注册
  • 避免使用IOC,注解反射的实现会导致性能下降
  • 使用RenderScript、OpenGL来进行复杂的绘制
  • 使用SurfaceView代替View进行大量、频繁的绘图

图片优化

  • 图片格式JPEG(不支持透明度)/PNG(体积大)/WebP
  • 图片的压缩:ImageAlpha有损压缩、TinyPNG有损压缩、ImageOptim无损压缩
  • 内存减少(inSampleSize缩放比例,ARGB4444,LRUCache,xml中加载的图片不会进行优化)
  • LRUCache:LinkedHashMap+最近最少使用算法(使用的时候把item移到队列头,队列尾即最近最少使用的了),回收对象只需将其remove,GC自然会回收(存在队列中则持有强引用,remove后则不可达)
  • 不用的时候recycle
  • inBitmap

电量优化

  • 应用切换到后台应该尽量避免一些不必要的操作,特别是BroadcastReceiver、Service中的一些操作
  • 数据传输:蓝牙传输、WIFI传输、移动网络传输。尽量做到:1、应用进入后台避免不必要的传输2、数据传输应根据业务进行合理的合并网络请求,避免轮询等
  • GPS定位,耗电
  • AlannManager系统级别的唤醒服务,比较耗电
  • WakaLock保持设备处于唤醒状态(即使用户长时间不操作),非常耗电

布局优化
- 避免overdraw,检测工具Enable GPU Overdraw
- 优化布局层级,View树尽量低
- 避免嵌套过多无用布局
- 多用来替代FrameLayout,减少不必要的层
- 重用
- view的延迟加载;用此可推迟布局加载
- 多用RelativeLayout少用LinearLayout,减少布局层次
- 工具HierarchyViewer;github.com/romainguy/ViewServer

网络优化

  • 避免DNS解析,使用IP访问代替域名访问
  • 合并网络请求
  • 预先获取数据
  • 避免轮询
  • 优化重连机制
  • 离线缓存
  • 压缩数据大小
  • 不同的网络环境使用不同的超时策略
  • CDN的使用

ListView优化

  • 重用converView
  • 减少findViewById
  • 避免在getView中做耗时操作
  • RecycleView代替ListView(对比)
  • 避免半透明元素(滑动过程消耗很大,可滑动过程不透明,滑动完再设置透明)和item的过度复杂。
  • 开启硬件加速

动画优化 CPU/GPU 内存
1.常规优化(图片优化 调用刷新的时机减少invalidate以及invalidate带参数 数据的提前准备 多个view做动画->一个view做多个动画)
2.逻辑优化 动画本身的实现方法 动画的状态控制
3.布局优化 不再改变或者消失的或者被遮住的背景的处理 尽可能减少layer
4.是否开启硬件加速,开启硬件加速多占用一部分内存
启动优化
1.冷启动优化:1.windowBackround设置为透明2.windowBackground设置欢迎图3.windowBackground设置为首页布局
2.Application里尽量不做初始化
3.oncreate 里不要做耗时操作 onstart可见 onresume可触
4.mat可以查看每个方法的执行时间和执行次数,可用于检测
○ 内存泄漏
■ Drawable.Callback引起内存泄漏
■ staitc中不要保存Drawable对象
■ 保存Drawable对象时,调用Drawable.setCallback(null)
■ Context泄漏
■ Handler泄漏
○ 转移图片内存地址;java虚拟机16M内存,Native4G内存。
■ 图片少:可将图片放到java heap中
■ 图片多:放到native中
● BitmapFactory.Options
○ -inNativeAlloc//图片内存放在native
■ BitmapFactory.Options.class.getField(inNativeAlloc).setBoolean(options,true);

原创粉丝点击