Android 經常使用開源框架源碼解析 系列 (六)LeakCanary 性能優化框架

1、前言 
    性能優化 是衡量 咱們app質量的一個很大標準
 
幾大影響性能的問題:
    UI卡頓 ——主線程耗時操做過多
    ANR——主線程耗時操做過多
    內存泄漏
    OOM 內存溢出——圖片處理
    啓動速度 
 
內存泄漏
特色:
  •  不易察覺,不易發現
  • 長時間不斷累積會致使OOM內存溢出
 
內存泄漏出現的緣由 
    Java虛擬機 會自動提供一套GC垃圾回收機制。在其內部會自動進行便利無用引用對象而進行內存的清理工做。
     其根本緣由是:較長生命週期的對象持有了較短生命週期的引用致使較短生命週期對象沒法被垃圾回收器回收。
 
GcRoots
    垃圾收集器 (Garbage Collector) 的對象
    
可達性算法-分析對象是否存活
     從GcRoots結點做爲起點,向下依次搜索,搜索走過的路徑被稱爲引用鏈。當一個對象達到沒有任何與GcRoots 引用鏈相連的時候,就說明該對象是不可用的,該對象就是Gc可回收對象。
 
Gc回收的對象:
    沒有被GcRoots 引用的對象
 
Java中的4種引用
 
  • 強引用
    若是一個對象是經過一串強引用鏈相連的 它就不能被回收。JVM即時報錯OOM也不會回收強引用對象
 
  • 軟引用 -soft
    優先級低於強引用,在內存足夠的狀況下,他和強引用的效果一致。可是若是出現內存不足的狀況,Gc垃圾回收就會回收掉軟引用對象。
 
  • 弱引用 -weak
    不會強制對象保存到內存當中,優先級低於軟引用。Gc回收器必回收對象之一。
 
  • 虛引用 -phantom
    代表這個引用的存在並沒有影響,任何狀況下都必須被Gc回收器回收。
 
在Eclipse中 MAT排查引用的方式:
    Path To GC Roots ——> exclude all phantom/weak/soft etc.references (排除以上引用鏈對象進行查找)    
   保證強引用的引用鏈上,沒有對象能夠被回收,代表沒有內存泄漏
 
LeakCanary 
 
特色:
    一、手動觸發GC 而後分析強引用的GC引用鏈
    二、若是存在GC引用鏈,說明有內存泄漏問題,會經過對話框提示用戶,並自動建立一個LC app
    三、會記錄每一次內存泄漏的GC引用鏈,經過它能夠直接定位到內存泄漏的未釋放的對象,並進行解決 
 
 
2、 內存泄漏基礎
 
檢查代碼是否存在內存泄漏的問題的工具
  •     MAT
  •     LeakCanary
    
LeakCanary
    
Square 開源的一款輕量級第三方內存泄漏檢測工具
    
  •     問題由誰產生
  •     致使了誰泄漏而不能被回收
 
核心原理
    watch 一個即將被銷燬的對象
 
內存
    一、棧內存stack 
        存放基本類型數據、對象的引用
    二、堆內存 heap
        存放new 建立出的對象 、數組;在這裏分配的內存是經過Gc垃圾回收器管理的
 ps:JVM 只有一個堆區,會被全部線程共享
 
    三、方法區(method 靜態區)
    同2同樣也是被全部線程共享的,包含全部的Class對象 和靜態變量
 
 
內存泄漏產生的緣由分析
  •  當一個對象已經不須要再使用了
        在這個對象該被回收的時候,而這時候正好有另一個正在使用的對象持有了這個應該被回收對象的引用而致使了這個對象不能被回收。致使本該被回收的對象不能被回收,而把該對象停留在heap 堆內存當中,就會產生內存泄漏
 
  • 有些對象只有優先的生命週期
    當生命周很短的對象在任務完成以後,就將被垃圾回收。若是在這類對象在生命週期本該結束的時候,這個對象還被一系列的引用就會致使內存泄漏。隨着泄漏的不斷累積,App會消耗完整個內存
 
內存泄漏致使的問題
 
    OOM
    形成OOM主要緣由之一;應用所須要的內存超過系統分配的內存最大值,形成內存溢出,致使App crash掉。
 
  3、Android 中常見的內存泄漏
 靜態內部類不持有外部累的引用 
 
    一、 單例形成的內存泄漏問題
錯誤寫法:
 
public class SingletonActivityContext {
    //靜態對象的生命週期和整個生命週期同樣長
    private static SingletonActivityContext instance;
    private Context context;
    //若是這個context 傳入的是Activity的context,當Activity退出的時候,這個context的內存並不會被回收
    //這個單例對象context 持有了傳入Activity對象的引用,因此Activity不能被gc回收
    private SingletonActivityContext(Context context) {
        this.context = context;
    }
    public static SingletonActivityContext getInstance(Context context) {
        if (instance == null) {
            instance = new SingletonActivityContext(context);
        }
        return instance;
    }
}
 
修改後的正確寫法:
/**
*解決辦法 ,傳入的context 爲Application context 就能解決問題錯誤的單例模式寫法
*/
public class SingletionApplicaitonContext {
    private static SingletionApplicaitonContext instance;
    private Context context;
 
    private SingletionApplicaitonContext(Context context) {
        this.context = context.getApplicationContext();//保證單例對象持有整個app的生命週期
    }
    public static SingletionApplicaitonContext getInstance(Context context) {
        if (instance == null) {
            instance = new SingletionApplicaitonContext(context);
        }
        return instance;
    }
}
二、非靜態內部類建立靜態實例形成的內存泄漏問題
/**
* 非靜態內部類建立靜態實例形成的內存泄漏
*/
public class StaticLeakActivity extends Activity {
    //爲例避免重複建立相同的數據對象定義一個靜態的全局變量,避免資源的重複建立
    private static nonStaticClass mResource;
 
    //非靜態內部類建立類一個靜態的實例mResource,致使該生命週期和app生命週期同樣長,
    // 會致使mResource一直持有StaticLeakActivity的引用 ,會形成該Activity沒法被gc回收
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main);
        if (mResource == null) {
            mResource = new nonStaticClass();
        }
    }
 
    /**
     * 非靜態的內部類nonStaticClass 默認會持有StaticLeakActivity的引用
     */
    private (static)class nonStaticClass {}
}
 
三、 handler形成內存泄漏問題
    爲了不Anr問題 而不在主線程進行耗時操做,處理網絡任務或是封裝一些請求回調的時候會經過handler 機制來進行處理。
/**
* 三、handler形成內存泄漏
* handler、message、messageQueue
* Message:handler發送消息的時候,這個message沒有被處理完成,這時候這個        message和它所發送的handler對象就將被線程一直持有
* TLS變量:handler生命週期和整個Activity生命週期不一致而形成內存泄漏
*/
public class HandlerLeakActivity extends Activity {
    //Message持有了Handler引用,而handler又持有了Activity的引用,致使Activity沒法被回收形成了內存泄漏,錯誤的寫法:
    private final Handler mLeakHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        mLeakHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //模擬延時10分鐘發送一個消息
            }
        }, 1000 * 60 * 10);
        //結束了Activity,但這時候還持有Activity的handler引用 由於延時操做finish掉的Activity就不會被回收
        finish();
    }
    /**
     * 解決辦法:
     * 一、將handler生命爲靜態的,使得handler生命週期和Activity無關
     * 二、經過弱引用的方式引入Activity
     * */
    privatefinal static  HandlermHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
}
四、線程形成的內存泄漏
/**
* 四、線程形成的內存泄漏
* asyncTask、Thread 的runnable定義成非靜態內部類的時候,當Activity想要被回收的時候,因爲任務持有了
* Activity的隱式引用,任務沒有完成致使當前Activity沒辦法被回收而致使內存泄漏
* 解決辦法:定義爲靜態內部類,避免Activity內存資源的泄漏
*/
public class ThreadLeakActivity extends Activity {
//靜態內部類static MyRunnable
staticclass MyRunnable implements Runnable{
…}
//靜態內部類static MyAsyncTask
staticclass MyAsyncTask extends AsyncTask<Void, Void, Void> {
    private WeakReference<Context> weakReference;
 
    public MyAsyncTask(Context context) {
        weakReference = new WeakReference<>(context);
    }
        ...
    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        ThreadLeakActivity activity = weakReference.
        if (activity != null) {
            //...
        }
    }
}
 
@Override
protected void onDestroy() {
    super.onDestroy();
   MyAsyncTask.cancel();//銷燬後臺可能在執行的任務,節省資源
}
五、WebView形成的內存泄漏
/**
* 五、WebView形成的內存泄漏
* Webview解析網頁的時候會申請native堆內存來保存頁面的元素。
* 頁面越複雜佔用的內存越多,當收集加載網頁過量的時候就會很是卡頓甚至app閃退 等
*/
public class WebviewLeakActivity extends AppCompatActivity {
    private WebView mWebView;
 
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        //...
    }
 
    @Override
    protected void onDestroy() {
        destroyWebView();
        //一、將WebView所處的Activity放在一個單獨的進程當中,檢查到app佔用內存過多的時候主動殺掉這個進程,系統就會自動回收這部份內存
    android.os.Process.killProcess((android.os.Process.myPid()));
        super.onDestroy();
    }
    private void destroyWebView() {
        if (mWebView != null) {
            mWebView.pauseTimers();
            mWebView.removeAllViews();
            mWebView.destroy();
        }
    }
}
 
3、 LeakCanary 
    目標:實時監控Activity
    
原理
    一、Activity Destroy以後將它放在一個WeakReference
    二、這個WeakReference關聯到一個ReferenceQueue
    三、查看ReferenceQueue是否存在Activity的引用
    四、若是該Activity泄漏,Dump出heap信息,而後分析泄漏路徑和根源
 
ReferenceQueue
軟引用/弱引用
對象被垃圾回收,JVM虛擬機就會把這個引用加入到與之關聯的引用隊列中。
 
源碼解析:
LeakCanary.install(this); //leakCanary入口
RefWatcher:用於監聽activity的內存泄漏
/**
*用於啓動activity RefWatcher類,activity RefWatcher類會在onDestroy()類後進行探測activity的內存泄漏
*/
public static RefWatcherinstall(Application application) {
  return install(application, DisplayLeakService.class);
}
public static RefWatcher install(Application application,
    Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  if (isInAnalyzerProcess(application)) {
    return RefWatcher.DISABLED;
  }
   //開啓一個通知內存泄漏信息的Activity
  enableDisplayLeakActivity(application);
  HeapDump.Listener heapDumpListener =
      new ServiceHeapDumpListener(application, listenerServiceClass);
   //A、建立一個RefWatcher 啓動一個Activity的RefWatcher,經過此監視activity的回收狀況
  RefWatcher refWatcher = androidWatcher(application, heapDumpListener);
 
  ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
  return refWatcher;
}
installOnIcsPlus():
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
    ...
    ActivityRefWatcher activityRefWatcher = new         ActivityRefWatcher(application, refWatcher);
    activityRefWatcher.watchActivities();
}
watchActivities()():
public void watchActivities() {
// 反註冊Activity 生命週期的Callback,保證之前所作的內存泄漏的內容要刪除。
  stopWatchingActivities();
  //從新註冊有關生命週期的callback
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
stopWatchingActivities(): 
public void stopWatchingActivities() {
  application.unregisterActivityLifecycleCallbacks
                                        (lifecycleCallbacks);
}
 
在lifecycleCallbacks中定義了不少的有關生命週期的內部類:
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
    new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityDestroyed(Activity activity) {
//B、經過ActivityLifecycleCallbacks把 ActivityRefWatcher與Activity的onDestroy生命週期進行了關聯操做
  ActivityRefWatcher.this.onActivityDestroyed(activity);
    }
};
 
void onActivityDestroyed(Activity activity) {
  refWatcher.watch(activity);
}
在refWatcher中含有的成員變量:
 final Executor watchExecutor;//用於執行內存泄漏檢測
 final DebuggerControl debuggerControl;//查詢是否正在調試中
    ps:若是在debug調試中就不會進行內存泄漏的檢測判斷
 final GcTrigger gcTrigger;//再一次執行處理垃圾Gc
 final HeapDumper heapDumper;//內存泄漏的堆信息文件
 final Set<String> retainedKeys;//持有待檢側的,和已經產生內存泄漏的key
 final ReferenceQueue<Object> queue;//引用隊列
    ps:判斷弱引用持有的對象是否已經被執行了Gc垃圾回收
 final HeapDump.Listener heapdumpListener;//堆信息監聽器
 
watch():
public void watch(Object watchedReference, String referenceName) {
  checkNotNull(watchedReference, "watchedReference");
  checkNotNull(referenceName, "referenceName");
  if (debuggerControl.isDebuggerAttached()) {
    return;
  }
  final long watchStartNanoTime = System.nanoTime();
    //對引用添加一個隨機數做爲惟一的key值
  String key = UUID.randomUUID().toString();
    //將獲取到的key加入到前面的持有待檢測以及產生內存泄漏引用的Set集合當中
  retainedKeys.add(key);
   //建立一個含有key值的弱引用
  final KeyedWeakReferencereference =
      new KeyedWeakReference(watchedReference, key, referenceName, queue);
   //C、開啓一個異步線程分析建立好的弱引用的內存泄漏問題
  watchExecutor.execute(new Runnable() {
    @Override public void run() {
      ensureGone(reference, watchStartNanoTime);
    }
  });
}
ensureGone()://確保Activity已經進入了Gone這個狀態(是否被真的回收了)
ps:在dump信息以前但願系統已經充分的進行了Gc垃圾回收而減小誤判的可能。
void ensureGone(KeyedWeakReference reference, 
                    long watchStartNanoTime) {
    …
//一、計算調用watch()函數到調用Gc垃圾回收總共產生的時間
long watchDurationMs 
    = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
 
//二、清除已經到達引用隊列的弱引用,把已經回收的key從set<>集合中移除
    removeWeaklyReachableReferences();
 
//三、若是處於debug狀態則不會進行內存泄漏分析 
    if (gone(reference) || debuggerControl.isDebuggerAttached()) {
  return;
}
 
//四、當前檢測對象沒有改變它的可達狀態,就會手動的調用Gc垃圾回收器進行垃圾回收
    gcTrigger.runGc();
 
//五、再次調用removeWeaklyReachableReferences清楚已經到達隊列的弱引用
removeWeaklyReachableReferences();
 
//六、若是此時對象尚未到達隊列那麼預期被垃圾回收的隊列可能會出如今這個隊列當中,若是沒有出現表示已經出現了內存泄漏了
    if (!gone(reference)) {
        …
    //建立內存泄漏的信息文件
File heapDumpFile = heapDumper.dumpHeap();
    …
 
//七、開始真正分析 內存泄漏的路徑
heapdumpListener.analyze(
    new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
        heapDumpDurationMs));
 
heapdumpListener.analyze():
@Override 
public void analyze(HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
runAnalysis():
處於IntentService當中,每次調用該方法都會回調到onHandleIntent()函數中
public static void runAnalysis(Context context, HeapDump heapDump,
    Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  Intent intent = new Intent(context, HeapAnalyzerService.class);
  intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
  intent.putExtra(HEAPDUMP_EXTRA, heapDump);
  context.startService(intent);
}
@Override protected void onHandleIntent(Intent intent) {
  String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
  HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
//堆內存的分析已經排除了系統內存所引發的內存泄漏
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
  AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
 
heapAnalyzer.checkForLeak():進一步分析內存。將前面建立好的.hprof文件輸入解析成內存快照snapshot對象輸出
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
    …
//根據須要檢測的類的key referenceKey的值來查詢結果中是否有須要的對象;
    獲取解析結果中檢測的對象
IObject leakingRef = findLeakingReference(referenceKey, snapshot);
//若爲空表示對象不存在,表示Gc的時候對象已經被清除了
if (leakingRef == null) {
  return noLeak(since(analysisStartNanoTime));
}
//若對象不爲空則 進入findLeakTrace() 查詢整個路徑產生內存泄漏
AnalysisResult result =
    findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);
 
if (!result.leakFound) {
  result = findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, false);
}
findLeakingReference():如何查找產生內存泄漏的引用,經過集合中的迭代器進行不斷的便利
private IObject findLeakingReference(String key, ISnapshot snapshot) throws SnapshotException {
leakCanary 是經過一個弱引用查找所須要的對象的
//一、建立一個class對象,針對須要檢測的類 構造了一個含有弱引用的對象,經過查找弱引用就能夠找到須要內存泄漏的對象,在snapshot快照中找到的第一個弱引用實際上就是
  Collection<IClass> refClasses =
      snapshot.getClassesByName(KeyedWeakReference.class.getName(), false);
//二、建立一個迭代器 獲取弱引用實例的惟一標識id
IClass refClass = refClasses.iterator().next();
int[] weakRefInstanceIds = refClass.getObjectIds();
18515600025
for (int weakRefInstanceId : weakRefInstanceIds) {
  IObject weakRef = snapshot.getObject(weakRefInstanceId);
    //三、經過for遍歷weakRefInstanceIds 數組獲得實例, 找到須要的key值
  String keyCandidate=
      PrettyPrinter.objectAsString((IObject) weakRef.resolveValue("key"), 100);
   //四、當查詢到Key值和最開始定義封裝的key值相等的時候,說明找到了須要的檢測對象,該對象就是內存泄漏的引用並返回該對象
  if (keyCandidate.equals(key)) {
    return (IObject) weakRef.resolveValue("referent");
  }
}
 
findLeakTrace():根據前面已經好到的內存泄漏的引用,找到最短泄漏內存路徑
private AnalysisResult findLeakTrace(long analysisStartNanoTime,
                ISnapshot snapshot,
                IObject leakingRef, 
                String className,
                boolean excludingKnownLeaks) throws SnapshotException {
 
  ExcludedRefs excludedRefs = excludingKnownLeaks ? this.excludedRefs : baseExcludedRefs;
    //不能被垃圾回收器回收的對象gcRootsTree 
  PathsFromGCRootsTree gcRootsTree = shortestPathToGcRoots(snapshot, leakingRef, excludedRefs);
    //找不到內存泄漏時候Gcroot的判斷,返回一個neLeak 返回值
  // False alarm, no strong reference path to GC Roots.
  if (gcRootsTree == null) {
    return noLeak(since(analysisStartNanoTime));
  }
    //生成的內存泄漏的調用棧,發生內存泄漏的時候展現在屏幕上的就是LeakTrace
  LeakTrace leakTrace = buildLeakTrace(snapshot, gcRootsTree, excludedRefs);
 
  return leakDetected(!excludingKnownLeaks, className, leakTrace, since(analysisStartNanoTime));
}
 
小結
    一、解析hprof文件,把這個文件封裝成snapshot內存快照
    二、根據弱引用和前面定義的key值,肯定泄漏的對象
    三、找到最短泄漏路徑,做爲結果反饋出來
 
4、 LeakCanary相關補充知識
    4.1 Application
  • Application-單例
    同四大組件同樣(4大組件都能獲取到application對象且獲取到同一份application對象)也是Android的一個系統組件用於存儲系統的一些信息,是生命週期最長的一個組件和app共生死。Application是一個單例對象,每個程序都只會建立一個單例對象。
    啓動Application系統會建立一個進程PID,在這個程序中的全部Activity都會在這個進程當中去運行。啓動時候會建立並實例化這個對象
  • Application是一個全局實例對象
Application 表明了一些全局的信息 ,application 繼承自ContextWrapper 
  • Application持有最長的生命週期
它的生命週期=App程序的生命週期。
 
Application的應用場景
    一、初始化全局對象、環境配置變量
    二、獲取應用程序當前的內存使用狀況,從而更合理的調配內存使用率,避免程序被系統kill掉
    三、監聽應用程序內全部Activity生命週期(對app activity生命週期進行全局的控制)
    四、監聽應用程序配置信息的改變
 
Application源碼
//Application的onCreate()方法默認狀況下是空實現,能夠繼承Application類而後重寫其onCreate()在其內進行初始化操做,和配置環境變量
public void onCreate() {
}
 
onTrimMemory():
通知應用程序 ,當前內存使用的狀況,同時他的狀況會根據內存級別來進行識別,根據內存使用狀況進行內存的不一樣程度的釋放
    內存級別劃分:
TRIM_MEMORY_COMPLETE,:內存不足,該進程在後臺進程列表的最後一個,立刻要被清理的內存對象
TRIM_MEMORY_MODERATE,:內存不足,該進程在後臺進程列表的中部,並不會被當即清理
TRIM_MEMORY_BACKGROUND,:內存不足,同時該線程是後臺進程
TRIM_MEMORY_UI_HIDDEN,:內存不足,而且這個進程的Ui已是不可見狀態
TRIM_MEMORY_RUNNING_CRITICAL,:內存不足,這個進程的優先級比較高,能夠優先清理該內存
TRIM_MEMORY_RUNNING_LOW,:內存不足,這個進程的優先級比較高,須要清理內存,優先級高於CRITICAL
TRIM_MEMORY_RUNNING_MODERATE,:內存不足,這個進程的優先級是後三個當中最高的,須要及時清理內存
        
onLowMemory():監聽android系統總體內存較低時候的回調方法,android4.0以前檢測系統內存狀況的函數
onConfigurationChanged():監聽應用程序配置信息發生改變時候的操做
onTerminate():應用程序結束的時候會回調這個方法
 
4.2 Android 性能數據上報
 4.2.1 性能接近思路
    a、監控性能狀況-基礎
    好比cpu、內存、卡頓、網絡流量,頁面加載流暢度等,量化數據指標
 
    b、根據上報統計信息 定位問題-方向
 
   c、持續監控並觀察-收集
    先後觀察多個版本查看數據的有效性來進行進一步優化
 
 4.2.2 性能種類
    a、資源消耗類
        電量、流量
    b、流暢度
  •   收集UI的流暢度體驗
  •   App啓動的時常
 
 4.2.1 各個性能數據指標
    思考:從哪幾個角度收集數據、統計、上報、定格問題解決
a、網絡請求流量測量
    經過運營商的網絡訪問Internet運營商替我媽的手機轉發數據報文(上下行,包含ip頭),數據報文的總大小字節量便是流量
    
   解決辦法:
    在平常開發中能夠經過tcpdump+Wireshark抓包測試,獲取手機root權限的電腦端抓包
    TrafficStats類-android SDK 提供的 linux讀取文件系統的文本解析
    ps:該類中經過一些靜態方法獲取到須要的一些信息
    
b、冷啓動 
    冷啓動 /熱啓動 區別: 進程舒服被kill掉
    冷啓動:沒有這個app進程,從0開始第一次啓動。或是說系統重啓後沒有該app的進程
    熱啓動:app從前臺切換到後臺,再返回就是熱啓動
 
命令行獲取冷啓動時間:
    adbshell am start -W packagename/MainActivity(首頁Activity)
 
日誌打印獲取冷啓動時間:
    起點->終點——》終點-起點 =冷啓動時間
    起點: Application的onCreate()方法中
    終點:首頁Activity的Oncreate()加載完成
 
c、卡頓
    1) Fps   幀率    
    幀率:頁面刷新的頻率
在Android中經過 Choreographer() 獲取幀率信息,記錄每一幀渲染的時間,下一幀再處理的時候不只能夠看是否有掉幀現象,並且整個檢測過程都是實時處理的
ps:只要出現掉幀現象就會出現Choreographer這個日誌在裏面
Choreographer():自己也會佔用性能
    VSYNC
    系統會發出垂直同步信號,能夠等同於硬件中斷意思。當發出信號後會通知整個界面進行重繪
每次同步的週期是16.6ms。一週的刷新頻率。若兩次繪製的間隔時間超過16.6ms 說明存在卡頓現象
 
    流暢度:
    =實際幀率/理論最優幀率
 
 
    2)主線程消息處理時長——衡量應用流暢度 很是重要!!並且有效的手段——debug版本或是小範圍模塊開發版本
 
原理: 在主線程消息先後都會用log信息打印消息輸出日誌,監測上下值的差是否小於閥值,小於說明正常,大於說明卡頓現象存在。
    根據設備不一樣設定不一樣的閥值!!
 
匹配消息信息自己也會形成性能的損耗,由於經過匹配日誌的字符串找到卡頓信息因此這個方法雖然能夠拿到用戶須要的信息可是沒法避免形成必定狀況下的損耗(字符串拼接的時候會產生不少臨時對象)。
 
d、內存佔用
RAM :通常意義上的整體內存,只須要知道整體內存
 
PSS:應用佔用的實際物理內存(系統會平均的分給每一個應用)
 
heap(最關心):虛擬機堆內存,與代碼的好壞有直接關係,heap分配不合理確定會影響app流暢度,由於GC會阻塞主線程形成卡頓
相關文章
相關標籤/搜索