首先推荐你们先阅读《Android 开发高手课》和我以前的三篇练习:html
最近二刷了《Android 开发高手课》,对于老师提到的一些案例,本身实践了一下。分享给学习此专栏的你们:git
这个是在第二课 崩溃优化(下)中提到的一个问题。github
固然,Google在Android 8.0修复了这个问题,其实就是捕获了一下异常:shell
try {
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
} catch (WindowManager.BadTokenException e) {
/* ignore */
}
复制代码
因此若是要避免这个问题,咱们就要找到HOOK点。7.1.1 Toast源码里面有一个变量叫 mTN,它的类型为 handler,咱们只须要代理它就能够实现捕获。json
简单实现代码以下:app
private void hook(Toast toast) {
try {
Field tn = Toast.class.getField("mTN");
tn.setAccessible(true);
Field handler = tn.getType().getField("mHandler");
handler.setAccessible(true);
Field callback = handler.getClass().getField("mCallback");
callback.setAccessible(true);
// 替换
callback.set(handler, new NewCallback((Handler) handler.get(tn.get(toast))));
} catch (Exception e) {
e.printStackTrace();
}
}
public class NewCallback implements Handler.Callback {
private final Handler mHandler;
public NewCallback(final Handler handler) {
this.mHandler = handler;
}
@Override
public boolean handleMessage(final Message msg) {
try {
this.mHandler.handleMessage(msg);
} catch (final RuntimeException e) {}
return true;
}
}
复制代码
在前一阵didi开源的booster
中也有对此问题的修复,看了后我以为考虑的更加完善。有兴趣的能够看看。dom
固然了,Toast
的小问题还有很多,相关的开源修复方案也不少,具体能够参考这篇文章。
这个是在第八课 启动优化(下)中提到的一个优化启动速度的方法。主要使用redex工具来实现,对redex用法不熟悉的,能够看我以前的文章。
adb shell ps | grep YOUR_APP_PACKAGE | awk '{print $2}'
复制代码
// 生成
adb shell am dumpheap YOUR_PID /data/local/tmp/SOMEDUMP.hprof
// 获取
adb pull /data/local/tmp/SOMEDUMP.hprof YOUR_DIR_HERE
复制代码
python redex/tools/hprof/dump_classes_from_hprof.py --hprof SOMEDUMP.hprof > list_of_classes.txt
复制代码
我在这里遇到了些问题,首先这个python脚本只支持python2,同时须要安装 pip、enum、enum34。不然会提示相似pip command not found
这样的错误。
pip是python的包管理工具,在Python2.7的安装包中,easy_install.py是默认安装的,而pip须要咱们手动安装
安装方法以下:
sudo easy_install pip
sudo pip install enum
sudo pip install enum34
复制代码
还有这个脚本不支持8.0以上的设备生成的DUMP.hprof
文件。
若是想获取8.0设备的类加载顺序,能够参考老师文中复写 ClassLoader
的方案。
以上完成后咱们就获取到了一个list_of_classes.txt
文件,大体格式以下:
com/bumptech/glide/load/resource/bitmap/BitmapResource.class
java/lang/Integer.class
android/graphics/Bitmap.class
libcore/util/NativeAllocationRegistry.class
java/lang/ref/FinalizerReference$Sentinel.class
java/lang/ref/FinalizerReference.class
libcore/util/NativeAllocationRegistry$CleanerThunk.class
sun/misc/Cleaner.class
libcore/util/NativeAllocationRegistry$CleanerRunner.class
android/graphics/Canvas.class
android/graphics/Paint.class
android/graphics/BitmapShader.class
android/graphics/RectF.class
java/util/TreeMap$TreeMapEntry.class
okhttp3/Request$Builder.class
okhttp3/Headers$Builder.class
java/util/ArrayList.class
okhttp3/HttpUrl$Builder.class
java/lang/String.class
java/lang/StringBuffer.class
android/icu/impl/ReplaceableUCharacterIterator.class
android/icu/text/ReplaceableString.class
okhttp3/HttpUrl.class
java/util/Collections$UnmodifiableRandomAccessList.class
okio/Buffer.class
java/lang/StringBuilder.class
java/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1.class
java/util/HashMap$EntryIterator.class
java/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry.class
okhttp3/Request.class
okhttp3/Headers.class
com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.class
...
复制代码
redex --sign -s test.jks -a key0 -p 111111 -c interdex.config -P proguard-rules.pro -o app_1.apk app.apk
复制代码
其中interdex.config
文件配置以下:
{
"redex" : {
"passes" : [
"InterDexPass"
]
},
"coldstart_classes" : "list_of_classes.txt"
}
复制代码
咱们可使用 010 Editor
来查看咱们的先后对比。
观察第二张图能够看到,就是咱们list_of_classes.txt
文件中的顺序。(Interdex Pass将忽略它在apk中找不到相应的类)
其实,Google在Android 8.0的ART优化中也有引用一个叫 dexlayout的来实现类和方法的重排,你们能够了解一下。
在第十三课 存储优化(中)中,老师提到文件遍历在 API 26 以后建议使用FileVisitor
替代 ListFiles
,由于文件遍历的耗时跟文件夹下的文件数量有关。老师文中说道:“曾经咱们出现过一次 bug 致使某个文件夹下面有几万个文件,在遍历这个文件夹时,用户手机直接重启了。”
通常的文件夹删除方法:
public static void deleteDir(String path) {
File dir = new File(path);
if (dir == null || !dir.exists() || !dir.isDirectory()){
return;
}
for (File file : dir.listFiles()) {
if (file.isFile()){
file.delete();
}else if (file.isDirectory()){
deleteDir(path);
}
}
dir.delete();
}
复制代码
就是利用dir.listFiles()
方法递归删除子文件。FileVisitor是Java7的新特性之一,在Android 8.0开始支持。因此完善后的删除文件方法以下:
public static void deleteDir(String path) throws IOException {
File dir = new File(path);
if (dir == null || !dir.exists() || !dir.isDirectory()){
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
//表示继续遍历
return FileVisitResult.CONTINUE;
}
/** * 访问某个path失败时调用 */
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
//若是目录的迭代完成而没有错误,有时也会返回null
if (exc == null) {
Files.delete(file);
return FileVisitResult.CONTINUE;
} else {
throw exc;
}
}
});
}else {
for (File file : dir.listFiles()) {
if (file.isFile()){
file.delete();
}else if (file.isDirectory()){
deleteDir(path);
}
}
}
dir.delete();
}
复制代码
第一次听到PrecomputedText
就是在二十一课 UI优化(下)中,它能够异步进行 measure 和 layout。我也写了一篇博客,有兴趣能够点击查看。
Litho 如我前面提到的 PrecomputedText 同样,把 measure 和 layout 都放到了后台线程,只留下了必需要在主线程完成的 draw,这大大下降了 U线程的负载。
具体的原理我就不介绍了,你们能够参看美团技术团队前一阵分享的文章:基本功 | Litho的使用及原理剖析。我主要说说个人使用过程。
我用Litho
的RecyclerCollectionComponent
实现了和我在PrecomputedText
博客中相同的例子。代码很简单,我就不贴出来了,有兴趣的能够查看Github。咱们直接看看效果:
帧数效果来讲略逊色PrecomputedText
,可是Litho
支持的组件更多,不仅仅是TextView
,还有Image
、EditView
等。而且它还能实现界面扁平化、占用更少的内存的优势。听说给美团App带来了不错的性能提高,官方文档也很详细,因此仍是值得你们尝试的。
好了,暂时就分享这么多了。若是对你有启发帮助,但愿能够点赞支持!