App性能优化

广播

** LocalBroadcastManager**

应用程序内部广播通信,优先采用LocalBroadcastManager,安全性更好,运行效率更高。

优势:平时常说BroadcastReceiver,采用的是Binder通信方式,这是跨进程的通信方式,系统资源消耗固然更多。而广播LocalBroadcastManager,采用的是Handler通信机制,Handler的实现是应用内的通信方式,所以效率与安全性都更高。

用法:

  1. 创建广播接收者
//广播类型
public static final String ACTION_SEND = "1";
//自定义广播接收者
public class AppBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
      //TODO
  }
}
//创建广播接收者
AppBroadcastReceiver appReceiver = new AppBroadcastReceiver();
  1. 注册广播
LocalBroadcastManager.getInstance(context).registerReceiver(appReceiver, new IntentFilter(ACTION_SEND));

注:LocalBroadcastManager注册广播只能通过代码注册的方式,而不能通过xml中静态配置,本地广播并没有走系统广播的流程。

  1. 发送广播
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_SEND));
  1. 取消广播
LocalBroadcastManager.getInstance(context).unregisterReceiver(appReceiver);

线程池

线程创建优先采用线程池ThreadPoolExecutor,而不是new Thread(); 另外设置线程优先级为后台运行优先级,能有效减少Runnable创建的线程和和UI线程之间的资源竞争。

优势:通过new Thread()来创建线程是比较常用的方式,而使用线程池的方式有不少优势如下

  • 线程可重复利用,节省线程的创建与销毁开销,性能有所提升;
  • 方便控制并发线程数,提高资源的利用率,减少过多的资源竞争;

用法:

//创建Runable对象
Runnable runnable = new Runnable() {
      @Override
      public void run() {
          android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
          //TODO
      }
  };
//创建线程池
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
  corePoolSize, maximumPoolSize,
  keepAliveTime, unit, workQueue);
//执行runnable
threadPoolExecutor.execute(runnable);

对于corePoolSize,一般往往可以设置为Runtime.getRuntime().availableProcessors(),代表当前系统活跃的CPU个数。

另外系统采用工厂模式,通过设置ThreadPoolExecutor的不同参数,提供四种默认线程池:

  1. ThreadPoolExecutor
    可缓存线程池,若线程空闲60s则回收,若无空闲线程可无限创建新线程,定义如下:
    new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    调用方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(runnable);
  1. ** newFixedThreadPool**定长线程,固定线程池大小,定义如下:
    new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    调用方法:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads);
fixedThreadPool.execute(runnable);
  1. ** newSingleThreadExecutor**只有一个线程的线程池,定义如下:
    new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
    调用方法:
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
newSingleThreadExecutor.execute(runnable);
  1. ** newScheduledThreadPool**可定时周期执行的线程池,定义如下:
    new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
    调用方法:
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(corePoolSize);
scheduledThreadPool.schedule(runnable, delay, TimeUnit.SECONDS);

ArrayList Vs LinkedList

ArrayList基于动态数组的数据结构, 对于随机访问(get/set),ArrayList效率比LinkedList高; LinkedList基于链表的数据结构,对于新增和删除(add/remove),LinedList效率比ArrayList高;

  1. 对于list, 优先选择ArrayList,除非少数需要大量的插入/删除操作才使用LinkedList。因为当数据量非常大时get操作,LinkedList时间复杂度为o(n), 而ArrayList时间复杂度为o(1)。

  2. 循环遍历
    LinkedList采用foreach方式, 效率最高。for循环方式效率大幅度降低。

List<Integer> list = new LinkedList<Integer>();
for (Integer j : list) {
  ... //TODO
}

ArrayList采用for循环+临时变量保存size,效率最高。 foreach方式效率略微降低。

List<Integer> list = new ArrayList<Integer>();
int len = list.size();
for (int j = 0; j < len; j++) {
  list.get(j);
}
  1. 采用new ArrayList()方式,初始大小为0,首次增加数组时,扩充大小到12,以后到数组需要增长时,会将大小增加50%,并将原来的成员全部复制到新的数组内。所以尽可能将ArrayList提前设置成目标大小,或者接近目标大小,以减少数组不断创建与复制的过程,提高效率。

HashMap Vs SparseArray

  1. 同时需要key和value,采用如下遍历方法:
Map<String, String> map = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
      entry.getKey();
      entry.getValue();
}
  1. 只需要获取key,采用如下遍历方法:
Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
  // key process
}
  1. 当HashMap的key是整型时,采用SparseArray,效率更高。避免了对key与value的自动装箱与解箱操作

Bitmap

  1. 使用BitmapFactory.Options对图片进行缩略读取;减小内存使用量;
  • inSampleSize:缩放比例,在把图片载入内存之前,先计算出一个合适的缩放比例,避免不必要的大图载入
  • decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,能减小内存空间
  1. 使用SoftReference:当内存不足时,虚拟机会自动回收它;
  2. 使用Bitmap.recycle()释放图片,虚拟机gc时回收Bitmap;
  3. 根据手机尺寸大小,配置不同大小的图片,保证使用尽可能小的图片资源。

Object Pool

内存对象,通过对象池技术来达到重复利用,减少对象重复创建。,从而减少内存分配和回收。

  1. 复用系统自带的资源,framework-res.apk中包含很多内置资源,比如字符串/颜色/图片/样式/布局等。可减少APK大小、内存开销。
  2. 缓存算法LRU

Job Scheduler

使用Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。

Android 避免使用Enum

Enum比静态常量,至少需要多过于2倍以上的内存空间,应该在Android中避免使用枚举。

onDraw()

由于onDraw方法调用比较频繁,需避免对象创建操作,因为迅速增加内存,同样引起频繁的gc,甚至内存抖动。

其他

  • 内部类引用导致Activity的泄漏,尤其是Handler
  • 监听器即使注销
  • 考虑使用Application Context而不是Activity Context
  • onLowMemory()与onTrimMemory()
  • 使用nano protobufs序列化数据
  • 使用IntentService
  • Adapter 利用convertView.getTag()与 ViewHolder
  • 窗口默认有一个不透明的背景,可以去掉的: getWindow().setBackground(null),或者修改xml
  • UI局部刷新
  • 在性能敏感的代码,避免创建Java对象。比如onMeasure(), onLayout(), onDraw(), getView()等
  • 使用弱引用

相关资料

http://developer.android.com/training/displaying-bitmaps/index.html
http://www.trinea.cn/android/hashmap-loop-performance/
http://hukai.me/android-performance-oom/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 当出现App启动慢、界面跳转慢、事件相应慢、滑动和动画卡顿、展现内容慢等问题的时候意味着App性能出现问题,这个时...
    Kurtis阅读 304评论 0 0
  • 一般我们写的app操作的数据多的时侯或者平时使用的时候都会经常出现卡顿、闪退、ANR停止运行等各种问题。这样会导致...
    踏雪羽翼阅读 565评论 0 1
  • 父母给予你善良的样貌 你跟随命运,成为一个善良的人 即使你是披着羊皮的狼 在众人眼里你仍是善良之辈 父母给予你老实...
    鹿原先生和蓬蒿阅读 302评论 6 9
  • Art艺生活阅读 773评论 0 0