Self Driven

兴趣是1,坚持是剩下的99

0%

《Android应用性能优化最佳实践》个毛线

正文

有好长一段时间没留意过(快有一年了说不定)市面上 Android 相关的开发书籍了。前几天面试完的时候领导突然让我给他推荐几本 android 开发相关的「好书」,我才又去亚马逊翻了一下排行榜。
《Android应用性能优化最佳实践》这本书给我留下了一点印象,作者好像很厉害,T家音乐的十年总监,评价也都是五星。唔,看起来不错,只是没看过不好评价。晚上写清单给领导的时候他竟然刚好也提到了这本书,这么多人推荐,大概不是雷,速度买了电子版晚上回去看。

结果还是踩到雷了= =

第一章就说 AS 多好多快的时候我就有点不愉快了。AS这种吃内存,每次更新都要折腾人的IDE还吹得那么好。即时现在稳定多了,但偶尔莫名其妙的问题,吃我16G内存这种事我还是受不了。

第二章开始我就发现了不少小错误,有些还是 Java 基础的问题,我开始有种「这作者行吗」的感觉。

而真正可怕的是从第三章开始。从他开始讲GC那一小节开始之后,我就基本看不下了。
这个人真的有了解过 Android GC 吗,感觉他就是在强行解释。无论是可达性,还是Android用的 GC 算法,每个都讲得特别扯淡。最可怕的是 GC 的分代模型他竟然是拿 Hotsport 的图片来讲的。喂喂喂,Hotspot 是什么,Dalvik 又是什么?Dalvik 里新生代还分了两个 Survivor ?而且还是用了 copy?糟点太多了根本数不过来。之后只要一讲到GC,我全选择跳过,受不了这么扯淡的。

后面那些章节,我还发现不少了未注明来源的「引用」。
首先是代码的引用。
监控检测那里说是介绍某些「监控框架」的代码,然后开始大段大段地改别人的开源项目,到最后也没写这个项目是什么。对比过他改后的代码,大致就是一些命名的区别,其他的基本一致。另外有区别的就是自己加了一个所谓的上报接口?
书里还有些是国外博客和官方文档的翻译。
最后两章讲 JobScheduler 跟 Doze 的那些内容没多少是原创的吧。话说,我真想说,翻译也得上心点,不要忘记翻「and」这种连接词,一看到这个词都没翻译,马上去搜原文,然后 bingo= =

整本书到底有多少是原创内容就不说了。就是有些观点跟数据,我真希望他能附个链接说下来源。
他说 HWRendering 有限制,还列了几点。这个官方没提到过,网上搜了一下,找到了一篇 2011 年的文章,这个可信,应该也就是照着这说的。但是他说 HWRendering 有缺陷,还说某些情况下不刷新。然而没有代码,没有 demo,也没有来源,这我就非常怀疑了。这种问题起码会是个 issue 吧,可是我也搜不到。这是不是个脑补的缺陷。

真庆幸这次买了电子版,不然我还得考虑怎么扔掉。
从15年到现在,国内出了不少 Android 相关的书了,但结果我能推荐的还是只有15年前看过的那几本。
而这还是一位十年总监写的优化书,再考虑之前某旅游应用总监写的某本书,我真的怀疑是不是因为国内有能力的人都不屑于写书。也确实,国内的书这么便宜,版税也收不了多少。可是还是希望哪天可以见到几本像样的 android 书啊(。

看本书要这么较真也是久违了,上次还是照着原文吐槽的垃圾回收的算法与实现= =不过那次只是吐槽翻译。

边看边整(tu)理(cao)的内容:

有种硬着头皮读下去的感觉,不知道的东西可以这么扯。5.6
按照章节的顺序写一下。

2.5.1 应用启动流程

  • 「每个应用只对应一个 Application 对象」– 多进程情况下会有多个。后面说的不同 Activity 和 Service 获得的对象都是同一个对象同理。
  • 「…重写的 Application 类后,看看该类可以重载的抽象接口」–Application 不是抽象类,没有抽象接口。
  • onTerminate() 应用结束时调用」 – 这个方法只有模拟器会调到。

2.6.3

  • 讲 ListView 的局部刷新说 notisfyDataSetChanged() 是什么鬼。。只有 RecyclerView 可以做列表的局部刷新吧。
    2.7.2
  • 「这时点击事件是绝对不会触发的」—这时你做了什么啊orz(在新位置点击控件)

2.8.2 (卡顿检测)代码实现

  • 实现 UiPerfMonitorConfig 竟然只是为了里面的常量。。。还有里面的 public 修饰符也是多余的。为什么不要实现只有常量的接口:constant interfaces (不过之前见过 JarkWharton 的代码也有定义常量接口这一习惯,确实没见过实现这种接口)
  • 这里就是把 BlockCanary 又重新造了一次轮子,而且应该说主要的代码都是一模一样的。唔,竟然没有出处说明 BlockCanary
  • LogWriter.writeLog4SampleFile(),从网上拷贝的代码注释没删掉(。

表示他一说到垃圾回收我就懵逼(。我真的不知道有哪些是可以信的了。
3.1.1 内存管理机制

  • 「回收器发现该对象不可达」-标记清除算法会有不可达对象回收,但是标记压缩算法是只对可达对象进行拷贝操作而已。说基于6.0了,5.0之后引进来的这个压缩操作不提一下?:gc-debug , android-gc
  • 把对象引用置为null可以提升回收效率?分情况吧。Android 的 VM 是有 JIT 的,局部变量置 null 没多少意义。。R大 null

3.1.3 内存回收机制

  • Android 的堆分代是这样子的?真希望他给个地址参考下,书里给的图片是 Hotspot JVM 的分代模型:java gc
  • 「Stop World是由于并发GC时,其他线程都会停止」。首先,这里是「stop the world」,然后这里是「并行」,并行跟并发并不一样。其次,STW是因为要标记从GC Root出来的对象,跟并不并行没关系。或者说并发就是尽量减少STW,跟 allocation 同时进行才叫并发。

3.1.4 GC类型

  • 「GC_FOR_MALLOC :Malloc GC」??只是因为没空间创建对象而进行GC,怎么变成这种定义了。GC LogMessages
  • 什么时候 Large Object Space 不在堆上了。从3.0之后,Bitmap就已经挪到了heap上。ART上的这块空间也只是堆的某部分而已:「Also, there is a separate heap for large objects like bitmaps, making it faster to find memory for these large objects without wading through the potentially fragmented regular heap. 」:understand the mobile context
  • 关于Java上内存泄漏的定义,更能接受 Effective Java 上提到的「unintentional object retention(无意识的对象持有)」这种说法。不是「GC没有把这些辨认出来」,而是你的姿势不对,GC没把该回收的回收掉那可是不尽责。

3.4.3 常见内存泄漏场景

  • 这里提到了 PhoneStateListener,补充下好了。按照官方说的使用 NONE 去注册就是反注册的意思,但实际并不会(试着用 LeakCanary检测下)。还是得使用 WeakReference。
    • PhoneStateListener 的内存泄漏只会在使用匿名内部类的情况下出现。这种泄漏可通过将成员变量引用置空或使用静态内部类解决。
  • 提到了 AsyncTask:「这种内存泄漏一般是临时性的」。这种说法就没法接受了。AsyncTask 在主线程初始化,doInBackground 在线程池的线程执行,最后还会在主线程回调,只要 Task 还在执行必定不会放开主线程的引用,如果之前说到 Looper 的 message 算内存泄漏,AsyncTask 这种会被另外线程引用的不是更糟?再考虑如果在有 Activity 重建的场景下用 AsyncTask,内存泄漏会更严重。
  • 「在应用中只要使用一次Webview,内存就不会被释放掉」喵喵喵???求来源,知道 WebView 是在另外一个线程且用 Activity 容易导致泄漏,可不释放是怎么回事,不是超严重的bug吗?

3.4.4 内存泄漏监控

  • 「(LeakCanary)编译器技术会检测无操作指令并出于优化的目的将无操作指令剔除」,releaseCompile 跟 testCompile 就是引用了一个相同方法名,但是方法实现为空的库而已。可是为什么我看不懂作者在说什么呢= =

3.5.4 图片内存优化

  • 「如inSampleSize为2时获得只有1/2大小的图片」,inSampleSize 是长度取样比例,2的话,图片大小是4分之一,因为长宽各除于2= =
  • 「而Android 4.4之后可以(使用inBitmap)重用任何bitmap的内存区域」:前提是这些 bitmap 都是 mutable 的:inBitmap
    • 另外,参考后面写的图片库,BitmapFactory.decodeStream 方法没有传入任何 options,这时返回的图片默认是 immutable 的,也就是根本用不了 inBitmap = =

3.6.1

  • 「所以增加了volatile关键字,用来保证MiniImageLoader对象只在主内存中加载。」,volatile 的语义有限制到这种地步吗。参照周志明的《深入理解Java虚拟机》,volatile的语义有 1:保证变量对所有线程的可见性;2:禁止指令重排序(不大清楚因为改成dex指令后Dalvik有没有实现二)。volatile只是保证了实例初始化之后其他线程不会再进入最后的null代码块而已。

3.6.2

  • 「Fresco,UIL等,都使用了“内存-本地-网络”三级缓存策略」:真的特别想问网络在什么时候成为客户端缓存了,而且还是常识?面过的人都跟我说缓存有三级。在网上搜 three level cache,基本没搜到有用的内容。Fresco 是提到了它们用三级缓存,但他们是「内存两层(应用内存,匿名共享内存),文件一层」啊。
    • 后面推荐了 UniversalImageLoader,随便提下,明明是17年还推荐这个有点过分了(。Glide更好,UIL 15年就停止更新了。

6.3 JobScheduler

  • 【出现了ThreadHandler= =还有其他很多奇怪的单词拼错了,是不是翻译的时候错了。
  • 这里说 JobSchedulerService 要实现两个方法,后面突然冒出个 jobFinished() 这第三个方法也是神奇了= =
  • JobScheduler(6.3.3.1)不少内容来自于这里,照着翻译的(中文夹杂了个「and」,这是漏翻了吧:D):JobScheduler ,官方文档也有一部分(ThreadHandler 也是因为翻译的时候少了一个斜杆导致的吧:JobService

6.4 Doze

  • 书里说是两个条件,但官方说的是「如果用户设备未插接电源、处于静止状态一段时间且屏幕关闭,设备会进入低电耗模式」,这是三个条件= =
  • 其他大概都是官方文档级别的说明。并没有多少实践。基本上所有把 targetSDK 设置得特别低的都不会使用这些内容吧= =

7.1

  • 「lib下可以包含4种类型」。唔,说是可以,好像也没错- -反正官方举的例子有这些「armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips.」。之后还有可能会增加,看厂商?

update

17.5.9

  • 修正 PhoneState 内存泄漏的说明。
  • 修正 mark-copy 的说法。copy 算法是将存活对象直接拷贝到另外分区(就是Survivor)的做法,不需要标记。

17.5.11

  • 修正「并发GC」这种说法。这是不存在的= =