1. 程式人生 > 其它 >android studio建立一個類繼承application_Android 騰訊 Matrix 原理分析(一):Matrix 概覽...

android studio建立一個類繼承application_Android 騰訊 Matrix 原理分析(一):Matrix 概覽...

技術標籤:android studio建立一個類繼承application

寫在前面

近期開始 Android Framework 層的學習,然而較為龐大的 Framework 讓人感覺無從下手。碰巧看到一篇文章說到騰訊的 效能監控框架 Matrix 用到了大量 Framework 相關的知識,所以試著分析下該框架的原始碼實現。

在學習大佬們程式碼的同時主要關注該框架用到了哪些、是怎麼使用的 Framework 的內容。

一、Matrix 簡介

官方說明

Matrix 是一款微信研發並日常使用的應用效能接入框架,支援iOS, macOS和Android。Matrix 通過接入各種效能監控方案,對效能監控項的異常資料進行採集和分析,輸出相應的問題分析、定位與優化建議,從而幫助開發者開發出更高質量的應用。

大公司就是大氣,直接雙端都給你整一套。

Matrix 地址

http://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2FTencent%2Fmatrix

Matrix for Android

Matrix-android 當前監控範圍包括:應用安裝包大小,幀率變化,啟動耗時,卡頓,慢方法,SQLite 操作優化,檔案讀寫,記憶體洩漏等等。

  • APK Checker: 針對 APK 安裝包的分析檢測工具,根據一系列設定好的規則,檢測 APK 是否存在特定的問題,並輸出較為詳細的檢測結果報告,用於分析排查問題以及版本追蹤

  • Resource Canary: 基於 WeakReference 的特性和 Square Haha 庫開發的 Activity 洩漏和 Bitmap 重複建立檢測工具

  • Trace Canary: 監控介面流暢性、啟動耗時、頁面切換耗時、慢函式及卡頓等問題

  • SQLite Lint: 按官方最佳實踐自動化檢測 SQLite 語句的使用質量

  • IO Canary: 檢測檔案 IO 問題,包括:檔案 IO 監控和 Closeable Leak 監控

好傢伙,功能還真不少。看樣子是個大工程,排個計劃吧:

  1. 首先是對框架的大致瞭解,對框架中用到的需要用到的類、函式進行預習;

  2. 從某一模組入手,分析功能實現的同時注重 Framework 的內容;

  3. 最後進行總結,思考為什麼這樣做,有沒有更好的做法。

那麼本文先大概瞭解一下框架,遇到 Framework 中的知識進行簡單的瞭解和預習。

二、使用 Matrix

有關 Matrix 的接入和使用官方文件已經寫得很清楚了,本文簡單總結下:

  1. 引入 Matrix 庫,新增相關依賴;

  2. 建立外掛監聽,可以接收到外掛的啟動和工作通知。
    Matrix 的功能基本都是由這些 外掛 Plugin 實現的,這樣做的好處一方面是解耦,另一方面是使用者可以根據需要選擇使用的功能。

  3. 在 Application 中初始化 Matrix,新增外掛並開啟外掛功能。

三、Matrix 結構

接下來根據 Matrix 的建立和使用來確定它的結構。

初始化

Matrix 需要在 Applicaton 中初始化,物件的構建方式是熟悉的建造者模式:

public class MatrixApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        // 建立 Matrix,傳入 application        Matrix.Builder builder = new Matrix.Builder(this);        // 設定外掛監聽        builder.patchListener(new TestPluginListener(this));        // 建立外掛        TracePlugin tracePlugin = new TracePlugin(new TraceConfig.Builder()                .build());        // 新增外掛        builder.plugin(tracePlugin);        // 初始化        Matrix.init(builder.build());        // 外掛開始工作        tracePlugin.start();    }}

維護的變數也比較簡單:

public static class Builder {    // 持有 Application     private final Application application;    // 外掛工作回撥    private PluginListener pluginListener;    // 維護外掛列表    private HashSet plugins = new HashSet<>();    public Builder(Application app) {        if (app == null) {            throw new RuntimeException("matrix init, application is null");        }        this.application = app;    }}
  • PluginListener:是一個介面,定義了外掛的生命週期。官方提供了預設實現 DefaultPluginListener,我們只需要繼承該類並設定給 Matrix 就可以接收到外掛的生命週期。

public interface PluginListener {    void onInit(Plugin plugin);// 初始化    void onStart(Plugin plugin);// 開始    void onStop(Plugin plugin);// 結束    void onDestroy(Plugin plugin);// 銷燬    void onReportIssue(Issue issue);// 提交報告}
public class TestPluginListener extends DefaultPluginListener {    public static final String TAG = "Matrix.TestPluginListener";    public TestPluginListener(Context context) {        super(context);    }    @Override    public void onReportIssue(Issue issue) {        super.onReportIssue(issue);        MatrixLog.e(TAG, issue.toString());        //add your code to process data    }}

  • plugins:外掛列表,使用 HashSet 維護,保證外掛不會重複新增。

外掛 Plugin

外掛是 Matrix 的重要組成結構,通過繼承抽象類 Plugin 來建立一個外掛,Plugin 是介面 IPlugin 的實現。IPlugin 介面定義了外掛所實現的主要功能:

public interface IPlugin {    Application getApplication();    void init(Application application, PluginListener pluginListener);    void start();    void stop();    void destroy();    String getTag();    void onForeground(boolean isForeground);}

比如分析卡頓的 TracePlugin,它就是一個繼承了 Plugin 的外掛實現,在工作的過程中也會呼叫這些方法。

Matrix 構造器

Matrix.Builder builder = new Matrix.Builder(this);Matrix.init(builder.build());

呼叫 builder.build() 之後會建立一個 Matrix 物件,然後建立一個用於監聽 App 生命週期的 AppActiveMatrixDelegate。之後遍歷所有的外掛列表,並呼叫它們的 init() 方法初始化外掛。

private Matrix(Application app, PluginListener listener, HashSet plugins) {    this.application = app;    this.pluginListener = listener;    this.plugins = plugins;    // 初始化    AppActiveMatrixDelegate.INSTANCE.init(application);    for (Plugin plugin : plugins) {        plugin.init(application, pluginListener);        pluginListener.onInit(plugin);    }}

AppActiveMatrixDelegate

這個類是個列舉單例,並且監聽應用 Activity 生命週期以及記憶體狀態。

public enum AppActiveMatrixDelegate {    // 1. 利用列舉建立單例    INSTANCE;    private static final String TAG = "Matrix.AppActiveDelegate";    private final Set listeners = new HashSet();    private boolean isAppForeground = false;    private String visibleScene = "default";    private Controller controller = new Controller();    private boolean isInit = false;    private String currentFragmentName;    private Handler handler;    public void init(Application application) {        if (isInit) {            MatrixLog.e(TAG, "has inited!");            return;        }        this.isInit = true;        // 2. HandlerTherad:一個封裝了 Handler 的執行緒        if (null != MatrixHandlerThread.getDefaultHandlerThread()) {            this.handler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper());        }        // 3. 註冊應用記憶體狀態回撥        application.registerComponentCallbacks(controller);        // 4. 註冊監聽 Activity 生命週期        application.registerActivityLifecycleCallbacks(controller);    }}
  1. 列舉實現單例的原理是利用列舉的特點來實現的:列舉型別是執行緒安全的,並且只會裝載一次。

  2. HandlerTherad 繼承了 Thread,可以看作是一個執行緒。而其內部維護了一個 Handler,在呼叫 start() 方法後初始化 Looper,可以很方便地執行非同步任務。其它類可以通過 getThreadHandler 方法獲取 HandlerTherad 的 Handler,然後 post 任務由 HandlerTherad 內部的 Looper 取出並執行。

  3. registerComponentCallbacks:Application 的方法,作用是監聽應用的記憶體狀態。
    在系統記憶體不足,所有後臺程式(優先順序為background的程序,不是指後臺執行的程序)都被殺死時,系統會呼叫 onLowMemory。
    OnTrimMemory 是 Android 4.0 之後提供的 API。比起 onLowMemory,這個回撥新增返回了一個 int 值表示當前記憶體狀態,開發者可以根據返回的狀態來適當回收資源避免 app 被殺死的風險。想要監聽記憶體狀態回撥需要實現 ComponentCallbacks2 介面,該介面是 ComponentCallbacks 的升級版。

應用記憶體優化之OnLowMemory&OnTrimMemory https://www.cnblogs.com/xiajf/p/3993599.html

  1. registerActivityLifecycleCallbacks:註冊監聽 Activity 狀態回撥,實現 Application.ActivityLifecycleCallbacks 介面以監聽 Activity 生命週期回撥。

Controller

Controller 實現 ComponentCallbacks2 監聽記憶體狀態、ActivityLifecycleCallbacks 監聽 Activity 生命週期。

private final class Controller implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {    @Override    public void onActivityStarted(Activity activity) {        // 1. 記錄啟動的 Activity        updateScene(activity);        // 1.1 告知 listeners Activity 在前臺了        onDispatchForeground(getVisibleScene());    }    @Override    public void onActivityStopped(Activity activity) {        // 1.2 獲取棧頂活動的 Activity        if (getTopActivityName() == null) {            // 1.3 告知 listeners Activity 在後臺了            onDispatchBackground(getVisibleScene());        }    }    ...    @Override    public void onTrimMemory(int level) {        MatrixLog.i(TAG, "[onTrimMemory] level:%s", level);        // 2. TRIM_MEMORY_UI_HIDDEN 表示當前 app UI 不再可見        if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback            onDispatchBackground(visibleScene);        }    }}

Controller 的邏輯主要為了區分 App 進入前臺或後臺。怎麼區分呢?

  • 有 Activity 回調了 onStart,說明 App 進入了前臺,記錄並返回給監聽就行;

  • 有 Activity 回調了 onStop,且棧頂沒有 Resume 狀態的 Activity,說明 App 進入了後臺;
    使用者點選了 Home 或者 Back 鍵,系統會通過 onTrimMemory 回撥一個 TRIM_MEMORY_UI_HIDDEN 狀態,告知這是 App 進入後臺,是回收資源的大好時機。

回撥 onStart 之後用一個字串記錄當前 Activity

private void updateScene(Activity activity) {    visibleScene = activity.getClass().getName();}public String getVisibleScene() {    return visibleScene;}

我們主要關注 onActivityStopped() 回撥中的 getTopActivityName() 方法,該方法用於獲取棧頂活動狀態的 Activity。

getTopActivityName()

public static String getTopActivityName() {    long start = System.currentTimeMillis();    try {        // 獲取 ActivityThread Class 物件        Class activityThreadClass = Class.forName("android.app.ActivityThread");        // 呼叫這個類的 currentActivityThread 方法,返回一個靜態的 ActivityThread 例項 sCurrentActivityThread        // 這個靜態例項是在 main 函式中賦值的        Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);        // 獲取 ActivityThread 的 mActivities 列表        // 在 Activity onCreate 之後,往列表新增 Activity 記錄        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");        activitiesField.setAccessible(true);        Map<Object, Object> activities; // 獲取 activityThread 類的 mActivities 物件        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {            activities = (HashMap<Object, Object>) activitiesField.get(activityThread);        } else {            activities = (ArrayMap<Object, Object>) activitiesField.get(activityThread);        }        if (activities.size() < 1) {            return null;        }        for (Object activityRecord : activities.values()) {            Class activityRecordClass = activityRecord.getClass();            Field pausedField = activityRecordClass.getDeclaredField("paused");            pausedField.setAccessible(true);            if (!pausedField.getBoolean(activityRecord)) {// onResume 的 Activity paused 為 false                Field activityField = activityRecordClass.getDeclaredField("activity");                activityField.setAccessible(true);                Activity activity = (Activity) activityField.get(activityRecord);                return activity.getClass().getName();            }        }    } catch (Exception e) {        e.printStackTrace();    } finally {        long cost = System.currentTimeMillis() - start;        MatrixLog.d(TAG, "[getTopActivityName] Cost:%s", cost);    }    return null;}

整個過程就是利用反射操作 Framework ActivityThread 的引數和函式,獲取棧頂的非 paused 狀態的 Activity。這段程式碼需要看著 ActivityThread 類慢慢消化,在你的 IDE 檢視或者線上檢視。

綜上,AppActiveMatrixDelegate 是利用內部的 Controller 監聽 App 發來的訊號,用來確定應用程式的前後臺狀態。

外部可以設定監聽,等應用程式前後臺轉換的時候再遍歷監聽者回調告知。

Issue

當外掛監控到 App 執行出現問題時,會把問題資訊封裝為一個 Issue 類進行報告。

public class Issue {    private int        type;    private String     tag;    private String     key;    private JSONObject content;    private Plugin     plugin;    public static final String ISSUE_REPORT_TYPE    = "type";    public static final String ISSUE_REPORT_TAG     = "tag";    public static final String ISSUE_REPORT_PROCESS = "process";    public static final String ISSUE_REPORT_TIME = "time";}

可以看到該類詳細記錄了問題的型別、資訊、外掛資訊等,發現問題是怎麼報告呢?我們拿效能監控外掛 TracePlugin 中的 FrameTracer 舉例:

FrameTracer

void report() {    float fps = Math.min(60.f, 1000.f * sumFrame / sumFrameCost);    MatrixLog.i(TAG, "[report] FPS:%s %s", fps, toString());    try {        // 根據外掛名稱遍歷查詢        TracePlugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);        if (null == plugin) {            return;        }        // ... 省略部分程式碼                JSONObject resultObject = new JSONObject();        resultObject = DeviceUtil.getDeviceInfo(resultObject, plugin.getApplication());        // 組裝內容        resultObject.put(SharePluginInfo.ISSUE_SCENE, visibleScene);        resultObject.put(SharePluginInfo.ISSUE_DROP_LEVEL, dropLevelObject);        resultObject.put(SharePluginInfo.ISSUE_DROP_SUM, dropSumObject);        resultObject.put(SharePluginInfo.ISSUE_FPS, fps);        Issue issue = new Issue();        issue.setTag(SharePluginInfo.TAG_PLUGIN_FPS);        issue.setContent(resultObject);        // 呼叫外掛方法        plugin.onDetectIssue(issue);    } catch (JSONException e) {        MatrixLog.e(TAG, "json error", e);    } finally {        sumFrame = 0;        sumDroppedFrames = 0;        sumFrameCost = 0;    }}

最後會呼叫 Plugin 的 onDetectIssue (Detect:發現、偵查出)方法傳遞 Issue 資訊。

Plugin # onDetectIssue

@Overridepublic void onDetectIssue(Issue issue) {    if (issue.getTag() == null) {        // 設定預設 tag        issue.setTag(getTag());    }    issue.setPlugin(this);    JSONObject content = issue.getContent();    // add tag and type for default    try {        if (issue.getTag() != null) {            content.put(Issue.ISSUE_REPORT_TAG, issue.getTag());        }        if (issue.getType() != 0) {            content.put(Issue.ISSUE_REPORT_TYPE, issue.getType());        }        content.put(Issue.ISSUE_REPORT_PROCESS, MatrixUtil.getProcessName(application));        content.put(Issue.ISSUE_REPORT_TIME, System.currentTimeMillis());    } catch (JSONException e) {        MatrixLog.e(TAG, "json error", e);    }    // 報告 Issue    pluginListener.onReportIssue(issue);}

這個 pluginListener 物件其實就是在 初始化 的時候建立並設定的 TestPluginListener,現在發現問題了就通過這個 Listener 報告問題。

public class TestPluginListener extends DefaultPluginListener {    public static final String TAG = "Matrix.TestPluginListener";    public TestPluginListener(Context context) {        super(context);    }    @Override    public void onReportIssue(Issue issue) {        super.onReportIssue(issue);        MatrixLog.e(TAG, issue.toString());        // 收到 Issue,做後續工作    }}

總結

畫個簡單的流程圖:

f9fabf4cf0cfebbcb7689667b763c222.png

流程

到這裡,Matrix 大致的工作流程已經搞清楚了。但是到現在基本沒有接觸核心功能,Matrix 是怎麼分析卡頓的?怎麼分析 ANR 的?... 後面會發文繼續分析,敬請期待。


作者:Marker_Sky
連結:https://www.jianshu.com/p/fc77b4807636

關注我獲取更多知識或者投稿

64a93e281fa61cd07dfc75a5451862ef.png

c00f91a86f2e39e8fca8e6bb21d73369.png