首先推薦你們先閱讀《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帶來了不錯的性能提高,官方文檔也很詳細,因此仍是值得你們嘗試的。
好了,暫時就分享這麼多了。若是對你有啓發幫助,但願能夠點贊支持!