• 滴滴开源的APM方案Dokit接入


    一、简介

    DoKit是一款面向泛前端产品研发全生命周期的效率平台,目前支持诸如Android、 iOS、小程序、Flutter和Web等多个平台。并且,随着dokit.cn平台端的推出,也标志着DoKit已经从单纯的效率工具正式进入了效率工具平台的阶段。与此同时,DoKit还在不断的更新,以提供更多的实用功能。

    目前,Dokit提供的功能有:

    平台工具(www.dokit.cn)

    1. 【数据Mock】 App接口Mock解决方案,提供一套基于App网络拦截的接口Mock方案,无需修改代码即可完成对于接口数据的Mock。
    2. 【健康体检】 一键式操作,整合DoKit多项工具,数据可视化,快速准确定位问题,让你对app的性能了如指掌。
    3. 【文件同步助手】 通过终端服务,让你的终端空间在平台端完整的展现并提供强大的文件以及数据库操作能力。
    4. 【一机多控】 主从同步,释放人力,让研发测试效率提升看得见

    常用工具

    1. 【App 信息查看】 快速查看手机信息,App 基础信息、签名相关、权限信息的渠道,避免去手机设置查找或者查看项目源代码的麻烦;
    2. 【开发者选项 Android特有】 一键跳转开发者选项,避免安卓由于平台差异导致的入口不一致
    3. 【本地语言】 一键跳转本地语言,避免安卓由于平台差异导致的入口不一致
    4. 【沙盒浏览】 App 内部文件浏览的功能,支持删除和预览, 并且能通过 AirDrop 或者其他分享方式上传到 PC 中,进行更加细致的操作;
    5. 【MockGPS】 App 能定位到全国各地,支持地图地位和手动输入经纬度;
    6. 【H5任意门】 开发测试同学可以快速输入 H5 页面地址,查看该页面效果;
    7. 【Crash查看】 方便本地打印出出现 Crash 的堆栈;
    8. 【子线程UI】 快速定位哪一些 UI 操作在非主线程中进行渲染,避免不必要的问题;(iOS独有)
    9. 【清除本地数据】 一键删除沙盒中所有数据;
    10. 【NSLog】 把所有 NSLog 信息打印到UI界面,避免没有开发证书无法调试的尴尬;
    11. 【Lumberjack】 每一条 CocoaLumberjack 的日志信息,都在在 App 的界面中显示出来,再也不需要导出日志这么麻烦;(iOS独有)
    12. 【DBView】 通过网页方便快捷的操作应用内数据库,让数据库的调试变得非常优雅;
    13. 【模拟弱网】 限制网速,模拟弱网环境下App的运行情况;(android独有)
    14. 【JS脚本】 在指定WebView运行JS脚本。(iOS独有)

    性能检测

    1. 【帧率】 App 帧率信息提供波形图查看功能,让帧率监控的趋势更加明显;
    2. 【CPU】 App CPU 使用率信息提供波形图查看功能,让 CPU 监控的趋势更加形象;
    3. 【内存】 App 内存使用量信息提供波形图查看功能,让内存监控的趋势更加鲜明;
    4. 【流量监控】 拦截 App 内部流量信息,提供波形图展示、流量概要展示、流量列表展示、流量筛选、流量详情,对流量信息统一拦截,成为我们 App 中自带的 “Charles”;
    5. 【卡顿】 锁定 App 出现卡顿的时刻,打印出对应的代码调用堆栈;
    6. 【大图检测】 通过流量监测,找出所有的大小超标的图片,避免下载大图造成的流量浪费和渲染大图带来的CPU消耗。
    7. 【启动耗时】 无侵入的统计出App启动过程的总共耗时;
    8. 【UI层级检查】 检查出每一个页面中层级最深的元素;
    9. 【函数耗时】 从函数级别分析app性能瓶颈;
    10. 【Load】 找出所有的Load方法,并给出耗时分析;(iOS独有)
    11. 【内存泄漏】 找出App中所有的内存泄漏的问题。

    视觉工具

    1. 【颜色吸管】 方便设计师 UI 捉虫的时候,查看每一个组件的颜色值是否设置正确;
    2. 【组件检查】 可以抓取任意一个UI控件,查看它们的详细信息,包括控件名称、控件位置、背景色、字体颜色、字体大小;
    3. 【对齐标尺】 参考 Android 系统自带测试工具,能够实时捕获屏幕坐标,并且可以查看组件是否对齐;
    4. 【元素边框线】 绘制出每一个 UI 组件的边框,对于组件布局有一定的参考意义。

    Weex专项工具(CML专项工具)

    1. 【console日志查看】 方便在端上查看每一个Weex文件中的console日志,提供分级和搜索功能;
    2. 【storage缓存查看】 将Weex中的storage模块的本地缓存数据可视化展示;
    3. 【容器信息】 查看每一个打开的Weex页面的基本信息和性能数据;
    4. 【DevTool】 快速开启Weex DevTool的扫码入口。

    在这里插入图片描述

    二、快速接入

    对于Android、iOS接入Dokit,官方详细的接入文档。

    接入步骤

    1. Gradle 依赖

    首先,我们打开app/build.gradle文件,然后添加如下依赖:

    dependencies {
        debugImplementation 'io.github.didi.dokit:dokitx:${lastversion}'
        releaseImplementation 'io.github.didi.dokit:dokitx-no-op:${lastversion}'
    }
    
    • 1
    • 2
    • 3
    • 4

    如果对版本不是很清楚,可以打开下面的链接进行查看:Android Releases版本

    滴滴内部业务:

    滴滴内部业务线接入请添加模块

    //数据mock内部网络库支持
    debugImplementation 'io.github.didi.dokit:dokitx-rpc:${lastversion}'
    //一机多控内部网络库支持
    debugImplementation 'io.github.didi.dokit:dokitx-rpc-mc:${lastversion}'
    
    • 1
    • 2
    • 3
    • 4

    最新版本参见这里
    需要说明的是,如果使用最新的版本,那么需要将你项目的gradle版本改为6.8版本以上,最好是7.0以上的版本。可以打开gradle-wrapper.properties文件进行修改。

    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists
    distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2. 初始化

    在 App 启动的时候进行初始化。

    overide fun onCreate() { 
       DoKit.Builder(this)
                .productId("需要使用平台功能的话,需要到dokit.cn平台申请id")
                .build()
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    3. 流量监控以及其他AOP功能(可选)

    AOP包括以下几个功能:
    1)百度、腾讯、高德地图的经纬度模拟
    2)UrlConnection、Okhttp 抓包以及后续的接口hook功能
    3)App 启动耗时统计
    4)慢函数
    5)大图

    在项目的 build.gradle 中添加 classpath。

    buildscript {
        dependencies {
            classpath 'io.github.didi.dokit:dokitx-plugin:${lastversion}'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在 app 的 build.gradle 中添加 plugin。

    apply plugin: 'com.didi.dokit'
    
    
    • 1
    • 2

    插件配置选项:
    添加到app module 的build.gradle文件下 与android {}处于同一级

    dokitExt {
        //通用设置
        comm {
            //地图经纬度开关
            gpsSwitch true
            //网络开关
            networkSwitch true
            //大图开关
            bigImgSwitch true
            //webView js 抓包
            webViewSwitch true
        }
    
        slowMethod {
            //调用栈模式配置 对应gradle.properties中DOKIT_METHOD_STRATEGY=0
            stackMethod {
                //默认值为 5ms 小于该值的函数在调用栈中不显示
                thresholdTime 10
                //调用栈函数入口 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的入口 假如不需要可以去掉该字段
                enterMethods = ["com.didichuxing.doraemondemo.MainDebugActivity.test1"]
                //黑名单 粒度最小到类 暂不支持到方法  千万不要用我默认的配置 如果有特殊需求修改成项目中自己的入口 假如不需要可以去掉该字段
                methodBlacklist = ["com.facebook.drawee.backends.pipeline.Fresco"]
            }
            //普通模式配置 对应gradle.properties中DOKIT_METHOD_STRATEGY=1
            normalMethod {
                //默认值为 500ms 小于该值的函数在运行时不会在控制台中被打印
                thresholdTime 500
                //需要针对函数插装的包名 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的项目包名 假如不需要可以去掉该字段
                packageNames = ["com.didichuxing.doraemondemo"]
                //不需要针对函数插装的包名&类名 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的项目包名 假如不需要可以去掉该字段
                methodBlacklist = ["com.didichuxing.doraemondemo.dokit"]
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    其中strategymethodSwitch配置项已经弃用。新的配置开关位于项目根目录下的gradle.properties中。

    具体的配置如下所示:

    // dokit全局配置
    // 插件开关
    DOKIT_PLUGIN_SWITCH=true
    // DOKIT读取三方库会和booster冲突 如果你的项目中也集成了booster 建议将开关改成false
    DOKIT_THIRD_LIB_SWITCH=true
    // 插件日志
    DOKIT_LOG_SWITCH=true
    // 自定义Webview的全限定名 主要是作用于h5 js抓包和数据mock
    DOKIT_WEBVIEW_CLASS_NAME=com/didichuxing/doraemonkit/widget/webview/MyWebView
    // dokit 慢函数开关
    DOKIT_METHOD_SWITCH=true
    // dokit 函数调用栈层级
    DOKIT_METHOD_STACK_LEVEL=4
    // 0:默认模式 打印函数调用栈 需添加指定入口  默认为application onCreate 和attachBaseContext
    // 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
    DOKIT_METHOD_STRATEGY=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    理由:
    为了减少项目的编译时间,所以慢函数的默认开关为false。再加上plugin的transform注册必须早于project.afterEvaluate。所以无法通过原先的配置项拿到配置信息,只能通过在全局的gradle.properties中的配置可以拿到。

    tips:
    当修改完DoKit插件的相关配置以后一定要clean一下重新编译才能生效。这是AS的缓存增量编译导致的,暂时没有其他好的解决方案。

    4. 自定义功能组件(可选)

    自定义组件需要实现 IKit 接口,该接口对应哆啦A梦功能面板中的组件。

    以代驾乘客端为例,实现环境切换组件如下。

    class DemoKit : AbstractKit() {
        override val category: Int
            get() = Category.BIZ
        override val name: Int
            get() = R.string.dk_kit_demo
        override val icon: Int
            get() = R.mipmap.dk_sys_info
    
        override fun onClickWithReturn(activity: Activity): Boolean {
            SimpleDoKitStarter.startFloating(DemoDokitView::class.java)
            return true
        }
    
        override fun onAppInit(context: Context?) {
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在初始化的时候注册自定义组件。

    override fun onCreate() {
        DoKit.Builder(this)
                .productId("需要使用平台功能的话,需要到dokit.cn平台申请id")
    	    .customKits(mapKits)
                .build()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    DoKit入口api

    public class DoKit private constructor() {
        companion object {
            
    
    
            /**
             * 主icon是否处于显示状态
             */
            @JvmStatic
            val isMainIconShow: Boolean
                get() = false
    
    
            /**
             * 显示主icon
             */
            @JvmStatic
            fun show() {
            }
    
            /**
             * 直接显示工具面板页面
             */
            @JvmStatic
            fun showToolPanel() {
            }
    
            /**
             * 直接隐藏工具面板
             */
            @JvmStatic
            fun hideToolPanel() {
            }
    
            /**
             * 隐藏主icon
             */
            @JvmStatic
            fun hide() {
            }
    
            /**
             * 启动悬浮窗
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            @JvmOverloads
            fun launchFloating(
                targetClass: Class<out AbsDokitView>,
                mode: DoKitViewLaunchMode = DoKitViewLaunchMode.SINGLE_INSTANCE,
                bundle: Bundle? = null
            ) {
            }
    
    
            /**
             * 启动悬浮窗
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            @JvmOverloads
            fun launchFloating(
                targetClass: KClass<out AbsDokitView>,
                mode: DoKitViewLaunchMode = DoKitViewLaunchMode.SINGLE_INSTANCE,
                bundle: Bundle? = null
            ) {
            }
    
            /**
             * 移除悬浮窗
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            fun removeFloating(targetClass: Class<out AbsDokitView>) {
            }
    
            /**
             * 移除悬浮窗
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            fun removeFloating(targetClass: KClass<out AbsDokitView>) {
            }
    
            /**
             * 移除悬浮窗
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            fun removeFloating(dokitView: AbsDokitView) {
            }
    
    
            /**
             * 启动全屏页面
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            @JvmOverloads
            fun launchFullScreen(
                targetClass: Class<out BaseFragment>,
                context: Context? = null,
                bundle: Bundle? = null,
                isSystemFragment: Boolean = false
            ) {
            }
    
            /**
             * 启动全屏页面
             * @JvmStatic:允许使用java的静态方法的方式调用
             * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
             */
            @JvmStatic
            @JvmOverloads
            fun launchFullScreen(
                targetClass: KClass<out BaseFragment>,
                context: Context? = null,
                bundle: Bundle? = null,
                isSystemFragment: Boolean = false
            ) {
            }
    
    
            @JvmStatic
            fun <T : AbsDokitView> getDoKitView(
                activity: Activity?,
                clazz: Class<out T>
            ): T? {
                return null
            }
    
            @JvmStatic
            fun <T : AbsDokitView> getDoKitView(
                activity: Activity?,
                clazz: KClass<out T>
            ): T? {
                return null
            }
    
            /**
             * 发送自定义一机多控事件
             */
            @JvmStatic
            fun sendCustomEvent(
                eventType: String,
                view: View? = null,
                param: Map<String, String>? = null
            ) {
            }
            /**
             * 获取一机多控类型
             */
            @JvmStatic
            fun mcMode(): WSMode {
                return WSMode.UNKNOW
            }
        }
    
    
        class Builder(private val app: Application) {
            private var productId: String = ""
            private var mapKits: LinkedHashMap<String, List<AbstractKit>> = linkedMapOf()
            private var listKits: List<AbstractKit> = arrayListOf()
    
            init {
            }
    
            fun productId(productId: String): Builder {
                return this
            }
    
            /**
             * mapKits & listKits 二选一
             */
            fun customKits(mapKits: LinkedHashMap<String, List<AbstractKit>>): Builder {
                return this
            }
    
            /**
             * mapKits & listKits 二选一
             */
            fun customKits(listKits: List<AbstractKit>): Builder {
                return this
            }
    
            /**
             * H5任意门全局回调
             */
            fun webDoorCallback(callback: WebDoorManager.WebDoorCallback): Builder {
                return this
            }
    
            /**
             * 禁用app信息上传开关,该上传信息只为做DoKit接入量的统计,如果用户需要保护app隐私,可调用该方法进行禁用
             */
            fun disableUpload(): Builder {
                return this
            }
    
            fun debug(debug: Boolean): Builder {
                return this
            }
    
            /**
             * 是否显示主入口icon
             */
            fun alwaysShowMainIcon(alwaysShow: Boolean): Builder {
                return this
            }
    
            /**
             * 设置加密数据库密码
             */
            fun databasePass(map: Map<String, String>): Builder {
                return this
            }
    
            /**
             * 设置文件管理助手http端口号
             */
            fun fileManagerHttpPort(port: Int): Builder {
                return this
            }
    
            /**
             * 一机多控端口号
             */
            fun mcWSPort(port: Int): Builder {
                return this
            }
    
            /**
             * 一机多控自定义拦截器
             */
            fun mcClientProcess(interceptor: McClientProcessor): Builder {
                return this
            }
    
            /**
             *设置dokit的性能监控全局回调
             */
            fun callBack(callback: DoKitCallBack): Builder {
                return this
            }
    
    
            /**
             * 设置扩展网络拦截器的代理对象
             */
            fun netExtInterceptor(extInterceptorProxy: DokitExtInterceptor.DokitExtInterceptorProxy): Builder {
                return this
            }
    
    
            fun build() {
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264

    开启插件调试的命令如下:

    ./gradlew :app:assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true
    
    • 1

    三、接入版本说明

    lastversion:3.5.0;kotlin编译插件为1.4.32 ;支持Gradle 6.8及以上
    lastversion:3.5.0.1; kotlin编译插件为1.3.72; 支持Gradle 6.8及以下

    DoKit最新版本描述
    3.3.5及以后的AndroidxdebugImplementation “io.github.didi.dokit:${aarName}: ${lastversion}”(1)dokitx的library和plugin的groupId及版本号需要保持一致;(2)AGP最低版本要求3.3.0+
    3.3.5及以前的Androidx版本debugImplementation “com.didichuxing.doraemonkit:${aarName}:3.3.5”(1)dokitx的library和plugin的groupId及版本号需要保持一致; (2)AGP最低版本要求3.3.0+
    支持android supportdebugImplementation “com.didichuxing.doraemonkit:${aarName}:3.3.5”support放弃更新,请大家尽快升级和适配Androidx

    ${aarName}需要改为指定的名称,参考如下:

    //核心模块
    
    debugImplementation "io.github.didi.dokit:dokitx:${lastversion}"
    
    //文件同步模块
    
    debugImplementation "io.github.didi.dokit:dokitx-ft:${lastversion}"
    
    //一机多控模块
    
    debugImplementation "io.github.didi.dokit:dokitx-mc:${lastversion}"
    
    //weex模块
    
    debugImplementation "io.github.didi.dokit:dokitx-weex:${lastversion}"
    
    //no-op 模块
    
    releaseImplementation "io.github.didi.dokit:dokitx-no-op:${lastversion}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • debugImplementation 需要根据自己的构建改成对应的productFlavor

    • 下面所有的例子均用dokitx举例。要使用support版本请将dokitx改为dokit即可。
      v3.3.5以后的版本需要添加mavenCentral()仓库

    源码链接:https://github.com/didi/DoKit

  • 相关阅读:
    Linux | 可重入函数 | volatile | SIGCHLD信号
    Netty-SocketIo 完美替换 nodejs 的 socketio
    Springboot整合WebScoket
    设计模式学习笔记
    第P6周—好莱坞明星识别(1)
    1、什么是复杂度?
    电脑重装系统后内存占用高怎么解决?
    如果面试时,问你职业规划怎么答?
    安卓10添加修改serial串口的服务
    上海亚商投顾:沪指冲高回落 纺织服装股午后集体走强
  • 原文地址:https://blog.csdn.net/xiangzhihong8/article/details/127394587