保活 先從老式最基礎的開始:html
內存三級緩存思路 對於列表頁中的圖片應使用LRU之類的內存緩存,對於activity和fragment銷燬時應該把其對應的圖片釋放掉。每一個圖片都是有依賴引用的,咱們通常默認是fragment或者是activity,當activity或者fragment銷燬時,咱們會將只依賴當前頁面的圖片移出LRU進行釋放。java
卡頓問題快速定位的方法GPU monitor分析:linux
**藍色偏高的常見緣由:**1. 動畫或者交互時緩存失效的太多,驗證方法是打出方法trace看看是否是有不少次的invalidate調用和dispatchDraw耗時在前面。2. 觸發了GC,驗證看看trace中主線程是否是被莫名的暫停了。3. 觸發了layout,這種藍色會很高,trace中measure方法會耗時較高。android
如何優化啓動速度 沒有閃屏頁activity,一旦存在閃屏頁activity那麼啓動速度就不大可能在200ms內跳過。 把window的背景設置爲閃屏頁,一旦MainActivity加載完畢就顯示主頁了。 雖然用戶也會看到一個相似的閃屏頁,但那個閃屏頁實際只是activity在theme中設置的background。 以前好像有人問我怎麼優化啓動速度。這個方案適合啓動畫面不是做爲廣告頁只是過渡頁使用的場景。git
<item name="android:windowBackground">@drawable/splash_logo</item>
複製代碼
自定義高性能可拖動GridView 在拖動過程當中沒有觸發過invalidate,也沒有觸發requestLayout,別的應用每次移動動畫會觸發notifyDateChange,這會觸發layout,影響性能。咱們在拖動過程當中會創建虛擬的視覺關係,只要不鬆手就會改變子view的順序,只有鬆手才觸發datechange。 提高性能繪製方法github
TextView優化web
Fragment留意點 每一個頁面都是Fragment,本身管理其生命週期和棧,每次啓動是以window的方式添加進來,進入動畫爲window動畫,手勢回退爲View動畫,爲了節省內存,頁面棧只保留2個對象,FragmentManager會進行回收釋放和Fragment的恢復。shell
狀態欄兼容注意點 針對狀態欄咱們單獨適配了4.4以上版本,5.0以上版本,6.0以上版本,Flyme系統,6.0以上的Flyme系統,MIUI系統,6.0以上的MIUI系統,YunOS系統,VIVO Funtouch 2.5如下版本和2.5以上版本.......canvas
try-catchapi
查看應用真實內存
adb shell dumpsys meminfo 你的包名 (monitor中不會顯示WebView的內存佔用)
(注意點)開了線程來執行耗時操做,但是這耗時操做執行的時候把主線的CPU佔用給搶了。。。。
高繪製性能函數 看一個ListView的函數
offsetChildrenTopAndBottom(int index)
複製代碼
這個方法性能很高,可是隱藏方法,listView移動子控件是用這個方法來移動的,它不破壞緩存,系統至關因而作了1+1+1+2,咱們本身作就是1+2+1+2+1+2,咱們本身寫ListView的時候發現了這個函數,咱們作for循環的性能仍是不如系統的這個隱藏方法。
使用windowManager的addView來添加控件 例子:經過調用Fragment的onCreateView來生成一個View,而後addView進來,這致使跳轉界面須要較長時間。如今得先addView一個View到windowManager中,而後在調用onCreateView,由於windowManager添加控件是在server進程,因此會當即addView進來,這時這裏的View就須要顯示點東西,這就須要相似windowBackground的東西來顯示。windowBackground它存在的目的就是爲了加快界面響應。
實現本身的windowbackground 先add一個相似decoreView進來,設置decoreView的background爲windowBackground,而後在往這個decoreView上添加實際的控件。
避免使用LayoutParams實現動畫
setLayoutParams()會觸發requestLayout()從而致使全部View從新measure、layout、draw,致使卡頓。 排查方法:能夠用佈局邊界排查大小變化的。 例:愛範兒 下拉刷新。(排查方法爲:重寫頂端控件的requestLayout方法,打上斷點,看看動畫或者交互過程當中誰調用到了這個方法)
還有查看誰刷新了頁面致使重繪的排查方法是:重寫頂端控件的invalidateChildInParent方法,看看誰調用到了。
使用硬件離屏緩存進行優化。(要保證緩存不失效)
正確的使用:顯示硬件層更新綠色閃一下。
錯誤的使用:過程當中一直綠色。
錯誤例:微信大圖。
正確例:掌閱首頁切換
複製代碼
主動釋放控件資源
//釋放佈局資源
private void releaseDradable(View view){
if (view instanceof ViewGroup) {
int count = ((ViewGroup)view).getChildCount();
for (int i=0;i<count;i++) {
View childView = ((ViewGroup)view).getChildAt(i);
if (childView instanceof ViewGroup) {
releaseDradable(childView);
}else {
releaseOneDrawable(childView);
}
}
}else {
releaseOneDrawable(view);
}
}
//釋放常見控件的資源,imageView是多種控件的根類
private void releaseOneDrawable(View view){
if (view !=null) {
BitmapDrawable src;
BitmapDrawable backGround;
if (view instanceof ImageView) {
src = (BitmapDrawable) ((ImageView) view).getDrawable();
recycleBitmap(src.getBitmap());
}
backGround = (BitmapDrawable) view.getBackground();
recycleBitmap(backGround.getBitmap());
}
}
private void recycleBitmap(Bitmap bitmap) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null; }
}
複製代碼
以上思路存在一個問題,即當某一個資源被多個activity引用時,回收該資源則會形成其餘持有該資源的activity發生異常。
維護一個Drawable鏈表用以記錄引用次數
將控件的getDrawable()和getBackground()設置爲null
系統Viewpager的性能優化
*這裏主要是優化ViewPager在添加和刪除item的時候,會觸發requestLayout致使的卡頓問題。第一次加載是沒有優化的,由於必須得觸發layout。 * 通過分析,咱們的場景不須要這個方法,就去掉了該方法, viewPager的adapter中instantiateItem()會執行container.addView(object),這也會觸發requestLayout;destroyItem也會觸發requestLayout。能夠替換爲attachViewToParent和detachViewFromParent方法來進行add和remove。這倆方法是listView中進行動態add和remove的方法,性能很高,不會讓緩存失效和觸發requestLayout。
咱們的ViewPager的setOffscreenPageLimit設置爲1。調用detachViewFromParent方法後爲了讓ViewPager從新錄製一下View的繪製,因此又手動調用了invalidate。 錄製繪製就是dispatchDraw流程,否則會走getDisplayList流程
在 instantateItem()中調用以下代碼
微信db打開方式 用戶設備的IMEI+uin值計算MD5值,注意是小寫字符,而後在取MD5的前7位字符構成的密碼。
關於RelativeLayout的使用 大量使用了RelativeLayout,致使了屢次mesure,一個relativelayout都要measure兩次,多個層次這種疊加以後,measure次數指數級上升。
關於 SoftwareRefrence 在android低版本上,SoftwareRefrence是遵循java標準的GC回收流程,即只有觸發GC的狀況爲內存不足時,纔會去檢查SoftReference,但在高版本上,SoftReference被檢查的更頻繁了,即不是隻有內存不足時纔去檢查,其存在的機率與WeakReference接近。
FragmentTabHost的問題 每次FragmentTabHost切換fragment時會調用onCreateView()重繪UI。 解決方法:
private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
TabInfo newTab = null;
for (int i = 0; i < mTabs.size(); i++) {
TabInfo tab = mTabs.get(i);
if (tab.tag.equals(tabId)) {
newTab = tab;
}
}
if (newTab == null) {
throw new IllegalStateException("No tab known for tag " + tabId);
}
if (mLastTab != newTab) {
if (ft == null) {
ft = mFragmentManager.beginTransaction();
}
if (mLastTab != null) {
if (mLastTab.fragment != null) {
// 將detach替換爲hide,隱藏Fragment
// ft.detach(mLastTab.fragment);
ft.hide(mLastTab.fragment);
}
}
if (newTab != null) {
if (newTab.fragment == null) {
newTab.fragment = Fragment.instantiate(mContext,
newTab.clss.getName(), newTab.args);
ft.add(mContainerId, newTab.fragment, newTab.tag);
} else {
// 將attach替換爲show,顯示Fragment
// ft.attach(newTab.fragment);
ft.show(newTab.fragment);
}
}
mLastTab = newTab;
}
return ft;
}
複製代碼
app啓動優化
以前優化軟件啓動時間的時候,就是Application的attachBaseContext()開啓method trace,首頁的dispatchDraw方法被調用以後關閉,而後這段時間CPU都幹了什麼。
耗時的操做放到了dispatchDraw方法以後post一個回調來執行。
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String x) {
Log.e("msg", x);
}
});
複製代碼
用這個來測算每一個消息花了多長時間,若是消息的執行時間超過了16ms,則獲取當前的函數調用棧。 (能夠確保你的耗時操做在頁面顯示以後才執行。)
關於inflate
inflate自己是io操做,而手機性能降低很大的一個緣由就是io性能變差。
動態添加能夠解決 xml 加載時間問題;自定義view 能夠解決嵌套層級問題。
strings文件下多個同種類型字符串的問題
RecyclerView的item的其餘思路
今日頭條跟手回退實現--羣分享記錄
今日頭條也是基於Activity的透明主題來實現的,可是這個方案都有兩個缺點,一就是疊加層級一多,滑動性能會降低明顯,基本疊加5層就很卡了,二是透明主題破壞了系統內存回收釋放的策略,致使全部的activity都是前臺Activity,系統都不會回收,就會OOM。解決這個問題有一個方案就是利用android 4.4裏提供的動態設置Activity透明主題來實現,當疊加了三層以後就將底部的第三層改成非透明主題。
/**
* 動態將一個activity設置爲不透明主題
*
* @param activity
*/
public static void convertActivityFromTranslucent(Activity activity) {
try {
Method method = Activity.class.getDeclaredMethod("convertFromTranslucent");
method.setAccessible(true);
method.invoke(activity);
} catch (Throwable t) {
}
}
複製代碼
/** * 動態將一個activity設置爲透明主題 * * @param activity */
public static void convertActivityToTranslucent(Activity activity) {
try {
Class<?>[] classes = Activity.class.getDeclaredClasses();
Class<?> translucentConversionListenerClazz = null;
for (Class clazz : classes) {
if (clazz.getSimpleName().contains(
"TranslucentConversionListener")) {
translucentConversionListenerClazz = clazz;
}
}
if (Build.VERSION.SDK_INT < 21) {//這個也僅支持4.4及以上
Method method = Activity.class.getDeclaredMethod(
"convertToTranslucent",
translucentConversionListenerClazz);
method.setAccessible(true);
method.invoke(activity, new Object[]{null});
} else {//5.0以上的系統
Method method = Activity.class.getDeclaredMethod(
"convertToTranslucent",
translucentConversionListenerClazz,
ActivityOptions.class);
method.setAccessible(true);
method.invoke(activity, null, null);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
複製代碼
反編譯了QQ空間的apk,他們把每一個類的構造函數中添加了一行Zygote.class.getName(),Zygote是系統中存在的一個類。他們選用Zygote是android SDK中不存在的,被隱藏的類,他們應該是認爲之後每一個版本系統中都會有這個類,因此才選擇了它。那麼系統進行檢查的時候就不會認爲初始化該類只須要使用當前Dex。(防止CLASS_ISPREVERIFIED )
animation有個onAnimationStart和onAnmationEnd方法裏面不可以使用addView/removeView的方法,有可用handler.postRunable()來執行;開啓硬件加速的時候有一些手機上會有機率性問題。硬件加速中draw流程只是進行錄製,若是在錄製的以後進行繪製的時候發現以前錄製的已經無效了,在4.X的機型上就可能發生崩潰。動畫的回調是在draw流程中執行的,在回調進行動態的removeView就會致使錄製的繪製命令無效。
線程的使用
ArrayList <File> list = new ArrayList<>();
public void scanDir(String dirString){
File dirFile = new File(dirString);
list.add(dirFile);
while (list.size() > 0){
File files[] = list.remove(0).listFiles();
for(File file : files){
if(file.isDirectory()){
list.add(file);
}else{
if(file.getAbsolutePath().endsWith("mp3")){
Log.e("", "這是音樂文件");
}
}
}
}
}
複製代碼
把遞歸變成隊列,再把隊列變成多線程執行,下面只須要開啓多個線程來執行scanDir(),好比一共有n+1(CPU核心數+1)個線程來執行scanDir(),當list有第一個元素時開啓一個線程執行scanDir(),這個線程會往list中繼續添加元素,當開啓的線程小於n+1時,繼續開啓線程,直到達到n+1,達到以後就等待線程執行完畢,其中某個線程執行完畢以後再次去list中獲取底部的元素來執行scanDir,直到list大小爲0。
ViewRootImpl
中的 setDrawDuringWindowsAnimating(true) 在
onAttachedToWindow後`調用(api 19及以上)相似的能夠有:private boolean sInited = false;
private Method msetDrawDuringWindowsAnimatingMethod;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && Build.VERSION.SDK_INT< 24) {
Object viewRootImpl = ((Activity)getContext()).getWindow().getDecorView().getParent();
if (!sInited) {
try {
msetDrawDuringWindowsAnimatingMethod = viewRootImpl.getClass().getMethod("setDrawDuringWindowsAnimating", boolean.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (msetDrawDuringWindowsAnimatingMethod != null) {
try {
msetDrawDuringWindowsAnimatingMethod.invoke(viewRootImpl, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
sInited = true;
}
}
}
複製代碼
耗時的工做完成的,把addViewInLayout之類的post到主線程就好了。 只有被添加到View樹上的時候纔可能會繪製,剛inflate出來的View沒有被添加到View樹上,因此不會進行measure和layout。measure是耗時的,若是在線程中執行,它就會減小主線程的卡頓,最後再添加到View樹上,而且不用觸發requestLayout。至關於異步加載view 使用場景:好比進入一個頁面開啓網絡加載一段內容,當網絡數據回來了就刷新頁面,若是這個頁面是列表,那麼若是是正在滑動的時候刷新頁面就會卡頓,這時就可使用這種方式來進行優化。
aapt dump badging demo.apk |grep version
aapt的其餘參數,也比較使用,好比向apl中插入文件,刪除文件,這個和unzip的效果是同樣同樣的。過去沒用appt時,咱們修改apk信息常常用zip/unzip,如今用appt也能夠搞定,還能避免有的系統沒有安裝zip/unzip的問題
aapt a demo.apk test.txt
Activity被銷燬分爲兩種:1. Activity對象被從ActivityThread中移除了,這時只是把java對象置null,若是你其餘地方還持有該對象,這個activity是不會被釋放的。 2. Activity所在的進程被回收,那它全部的資源都被回收了。只會有一些可序列化的數據被保存。
如下條件webview能夠關閉硬件加速
//mModelNumber = Build.MODEL;
複製代碼
能夠利用Looper的log機制和添加全局控件來本身實現那個柱狀圖。
列表優化思路 1.item中不能使用任何xml,包括xml的drawable;2.減小View個數層級下降過分繪製;3.本身實現TextView,系統的TextView(特別是android 7.0如下系統)性能太爛。
anr adb shell ls /data/anr/
adb pull /data/anr/traces.txt ~/Desktop
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
該思想可用於多種自測內存泄漏的場景。
若是是CPU密集型應用,則線程池大小設置爲N+1, 若是是IO密集型應用,則線程池大小設置爲2N+1
若是一臺服務器上只部署這一個應用而且只有這一個線程池,那麼這種估算或許合理,具體還需自行測試驗證。可是,IO優化中,這樣的估算公式可能更適合:最佳線程數目 =((線程等待時間+線程CPU時間)/線程CPU時間 )* CPU數目由於很顯然,線程等待時間所佔比例越高,須要越多線程。線程CPU時間所佔比例越高,須要越少線程。下面舉個例子:好比平均每一個線程CPU運行時間爲0.5s,而線程等待時間(非CPU運行時間,好比IO)爲1.5s,CPU核心數爲8,那麼根據上面這個公式估算獲得: ((0.5+1.5)/0.5)*8=32
。這個公式進一步轉化爲:最佳線程數目 = (線程等待時間與線程CPU時間之比 + 1)* CPU數目