腾讯 Apm 框架 Matrix 源码阅读 - 架构解析

版本

v0.6.5

温馨提示

配合 推荐 Matrix 源码完整注释
可能会有更好的效果

概述

本篇文章是 腾讯开源的 APM 框架 Matrix 系列文章的第二篇,将对 matrix-android-lib这个模块的代码进行阅读。这个模块主要是对Matrix 插件架构进行构建。上一篇为Matrix 源码阅读 - gradle插件

先看一下类图及类功能的简介。
Matrix整体架构.jpg

  • Matrix:Matrix整个框架运行期的入口。也控制着插件的生命周期。所有插件都需要注册到Matrix这个类中进行统一管理
  • AppActiveMatrixDelegate:相当于Application的代理,本身可以感知Activity生命周期,和 APP 进入前台还是 退出到后台 ,感知到以后 会通知给 IAppForeground
  • IPlugin:定义了所有 Plugin的生命周期及通用方法
  • IssuePublisher.OnIssueDetectListener:只有一个onDetectIssue方法,当需要上报问题时会被调用
  • IAppForeground: 只有一个方法就是onForeground 会被 AppActiveMatrixDelegate 调用,可以感知 APP 进入前台还是 退出到后台
  • Plugin: 实现了IPlugin,ssuePublisher.OnIssueDetectListenerIAppForeground。是所有插件的父类,本身兼具插件的 生命周期,感知 问题和 APP状态切换的功能。具体实现有TracePlugin,IOCanaryPlugin,ResourcePlugin,SQLiteLintPlugin后面我们会挨个分析。

下面我们就从 Matrix这个入口类开始看。

1. Matrix

我们使用Matrix一般会这样初始化

    Matrix.Builder builder = new Matrix.Builder(this);
    builder.plugin(new TracePlugin());
    builder.plugin(new IOCanaryPlugin());
    .....
    //详见【1.1】
    Matrix matrix=builder.build();
    //详见【3.1】
    matrix.startAllPlugins();

1.1 Matrix构造方法

看到builder 就知道是使用建造者模式来创建Matrix对象,所以我们二话不说直接看Matrix的构造方法

    private Matrix(Application app, PluginListener listener, HashSet<Plugin> plugins) {
        //保存builder传入的参数
        this.application = app;
        this.pluginListener = listener;
        this.plugins = plugins;
        //初始化Application的代理对象 详见【1.2】
        AppActiveMatrixDelegate.INSTANCE.init(application);
        for (Plugin plugin : plugins) {
            //调用plugin的init方法 详见【2.1】
            plugin.init(application, pluginListener);
            //回调plugin onInit生命周期
            pluginListener.onInit(plugin);
        }

    }

可以看到Matrix的构造方法中,做了三件事

  1. 首先是将builder中传入的参数保存到自己的成员变量中,其中最重要的就是HashSet中存储的各个Plugin.
  2. 初始化 AppActiveMatrixDelegate 这个Application的代理对象
  3. 循环调用所有Plugin的init方法

1.2 AppActiveMatrixDelegate.init()

值得说一下的是 AppActiveMatrixDelegate 这个类是个 枚举单例,不了解枚举单例好处及和其他几种单例方式区别的同学,可以自行百度,谷歌一下,网上文章一大把。
我们继续看AppActiveMatrixDelegate.init()

    //详见【1.3】
    Controller controller = new Controller();

    public void init(Application application) {
        //保证只初始化一次
        if (isInit) {
            return;
        }
        this.isInit = true;
        //获取 子线程 handler
        this.handler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper());
        //监听 onConfigurationChanged 和 onLowMemory
        application.registerComponentCallbacks(controller);
        //监听activity生命周期
        application.registerActivityLifecycleCallbacks(controller);
    }

可以看到在init()方法中, application注册了ActivityLifecycleCallbacksComponentCallbacks2这两个监听,并且这两个监听指向了同一个对象Controller.有了这两个监听ActivityLifecycleCallbacks就具有了 感知Activity生命周期和APP活动的能力。

1.3 Controller

private final class Controller implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

        @Override
        public void onActivityStarted(Activity activity) {
            //更新当前页面名称 详见【1.4】
            updateScene(activity);
            //广播 APP切换到前台的 事件 详见【1.5】
            onDispatchForeground(getVisibleScene());
        }
        @Override
        public void onActivityStopped(Activity activity) {
            if (getTopActivityName() == null) {
                 //广播 APP切换到后台的 事件 详见【1.6】
                onDispatchBackground(getVisibleScene());
            }
        }
        .....
        @Override
        public void onTrimMemory(int level) {
            MatrixLog.i(TAG, "[onTrimMemory] level:%s", level);
            if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback
                 //广播 APP切换到后台的 事件
                onDispatchBackground(visibleScene);
            }
        }
    }

可以看到Controller是真正感知APP状态的类。

1.4 Controller .updateScene()

    private void updateScene(Activity activity) {
      //记录当前activity的名称
        visibleScene = activity.getClass().getName();
    }

1.5 Controller .onDispatchForeground()

  private void onDispatchForeground(String visibleScene) {
      //如果APP已经是可见的,就不做任何操作
        if (isAppForeground || !isInit) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                //修改APP状态为可见
                isAppForeground = true;
                synchronized (listeners) {
                    for (IAppForeground listener : listeners) {
                        //通知给各个监听者
                        listener.onForeground(true);
                    }
                }
            }
        });

    }

1.6 Controller .onDispatchBackground()

 private void onDispatchBackground(String visibleScene) {
        //如果APP已经是不可见的,就不做任何操作
        if (!isAppForeground || !isInit) {
            return;
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                isAppForeground = false;
                synchronized (listeners) {
                    for (IAppForeground listener : listeners) {
                        //通知给各个监听者
                        listener.onForeground(false);
                    }
                }
            }
        });


    }

2.1Plugin.init()

Matrix的构造方法中AppActiveMatrixDelegate对象初始化完成后就调用了Plugin.init()方法

    @Override
    public void init(Application app, PluginListener listener) {
        ....
        //将plugin当前的状态切换到 PLUGIN_INITED
        status = PLUGIN_INITED;
        this.application = app;
        this.pluginListener = listener;
        //将自己注册到  AppActiveMatrixDelegate 中 详见【2.2】
        AppActiveMatrixDelegate.INSTANCE.addListener(this);
    }

2.2 AppActiveMatrixDelegate.INSTANCE.addListener(this);

可以看到Plugin在init的时候将自己注册到了AppActiveMatrixDelegate中,当APP前后台状态改变是 Plugin就能感知到

    public void addListener(IAppForeground listener) {
        synchronized (listeners) {
            //添加监听器
            listeners.add(listener);
        }
    }

3.1 Matrix.startAllPlugins()

startAllPlugins这个方法比较简单就是启动所有的插件,Matrix作为插件的管理者理还具有其他类似的方法如 stopAllPlugins,destroyAllPlugins

    //启动所有插件
    public void startAllPlugins() {
        for (Plugin plugin : plugins) {
            plugin.start();
        }
    }

Plugin.onDetectIssue()

  //组织 问题的 Json数据 并调用 监听的 onReportIssue 方法
    @Override
    public void onDetectIssue(Issue issue) {
        if (issue.getTag() == null) {
            // set default 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);
        }

        //MatrixLog.e(TAG, "detect issue:%s", issue);
        pluginListener.onReportIssue(issue);
    }

Plugin提供了简单的统一的数据上报方法,并对上报数据封装成统一格式的Json字符串。

总结:

  1. Matrix是整个框架的入口也是所有插件的管理者
  2. 所有的插件都需要继承PluginPlugin本身已经具备了 问题上报和感知APP前后台状态的能力。

系列文章

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 119,054评论 1 241
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 51,980评论 1 200
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 74,402评论 0 167
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 36,473评论 0 127
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 43,290评论 1 206
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 35,928评论 1 126
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 27,913评论 2 207
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 27,039评论 0 120
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 26,005评论 5 173
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 30,108评论 0 178
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 27,307评论 1 170
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 28,560评论 1 178
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 22,865评论 0 25
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 25,485评论 2 165
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 29,341评论 3 173
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 24,325评论 0 4
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 24,351评论 0 113
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 30,542评论 2 189
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 30,958评论 2 188

推荐阅读更多精彩内容