5. 第三方模块

5.1 Crash(XCrash)

Crash监控崩溃后的堆栈上传,作者采用接入爱奇艺的XCrash框架

源码分析

1、启动Crash的监控

Crash的监控通过反射启动XCrash框架,因为XCrash的框架引入,可能会和你的项目有些框架的冲突,作者将XCrash的初始化代码放置在另一个Module中,这样方便热插拔当前的崩溃框架

public class Crash extends ProduceableSubject<List<CrashInfo>> implements Install<CrashConfig> {private boolean mInstalled;private CrashConfig mConfig;@Overridepublic synchronized boolean install(final CrashConfig crashContext) {if (mInstalled) {L.d("Crash already installed, ignore.");return true;}Consumer<List<CrashInfo>> consumer = this::produce;// auto detect crash collector providertry {ReflectUtil.invokeStaticMethodUnSafe("cn.hikyson.android.godeye.xcrash.GodEyePluginXCrash", "init",new Class<?>[]{CrashConfig.class, Consumer.class}, new Object[]{crashContext, consumer});} catch (Exception e) {L.d("Crash can not be installed:", e.getLocalizedMessage());return false;}mConfig = crashContext;mInstalled = true;L.d("Crash installed.");return true;}
}

2、采集Crash信息

Crash初始化主要参考XCrash文档

public class GodEyePluginXCrash {/*** entrace** @param crashContext* @param consumer*/public static void init(CrashConfig crashContext, Consumer<List<CrashInfo>> consumer) {ICrashCallback callback = (logPath, emergency) -> {try {sendThenDeleteCrashLog(logPath, emergency, crashContext, consumer);} catch (IOException e) {L.e(e);}};XCrash.init(GodEye.instance().getApplication(), new XCrash.InitParameters().setAppVersion(getAppVersion(GodEye.instance().getApplication())).setJavaRethrow(true).setJavaLogCountMax(10).setJavaDumpAllThreadsWhiteList(new String[]{"^main$", "^Binder:.*", ".*Finalizer.*"}).setJavaDumpAllThreadsCountMax(10).setJavaCallback(callback).setNativeRethrow(true).setNativeLogCountMax(10).setNativeDumpAllThreadsWhiteList(new String[]{"^xcrash\\.sample$", "^Signal Catcher$", "^Jit thread pool$", ".*(R|r)ender.*", ".*Chrome.*"}).setNativeDumpAllThreadsCountMax(10).setNativeCallback(callback).setAnrRethrow(true).setAnrLogCountMax(10).setAnrCallback(callback).setPlaceholderCountMax(3).setPlaceholderSizeKb(512).setLogFileMaintainDelayMs(1000));Schedulers.computation().scheduleDirect(() -> {try {sendThenDeleteCrashLogs(consumer);} catch (Exception e) {L.e(e);}});}
}

Crash的初始化之后,会通过sendThenDeleteCrashLogs获取当前已有的崩溃堆栈信息,参数consumer就是通过反射传递过来的日志器,如果当前存在Crash的信息,则获取XCrash的崩溃信息,将Crash信息封装后发送出去

private static void sendThenDeleteCrashLogs(Consumer<List<CrashInfo>> consumer) throws Exception {File[] files = TombstoneManager.getAllTombstones();List<CrashInfo> crashes = new ArrayList<>();for (File f : files) {try {crashes.add(wrapCrashMessage(TombstoneParser.parse(f.getAbsolutePath(), null)));} catch (IOException e) {L.e(e);}}if (!crashes.isEmpty()) {L.d("Crash produce message when install, crash count:%s", crashes.size());consumer.accept(crashes);TombstoneManager.clearAllTombstones();}
}

3、总结

借助当前的第三方的优秀框架,通过热插拔的形式,引入到我们的性能监控中

5.2 LeakCanary

LeakCanary监控应用的内存泄漏信息,作者采用接入LeakCanary框架

源码分析

1、启动LeakCanary的监控

LeakCanary的监控通过反射启动LeakCanary框架

public class Leak extends ProduceableSubject<LeakInfo> implements Install<LeakConfig> {private boolean mInstalled;private LeakConfig mConfig;@Overridepublic synchronized boolean install(LeakConfig config) {if (mInstalled) {L.d("Leak already installed, ignore.");return true;}mConfig = config;try {ReflectUtil.invokeStaticMethodUnSafe("cn.hikyson.android.godeye.leakcanary.GodEyePluginLeakCanary", "install",new Class<?>[]{Application.class, Leak.class}, new Object[]{GodEye.instance().getApplication(), this});} catch (Exception e) {L.d("Leak can not be installed, please add android-godeye-leakcanary dependency first:", e);return false;}mInstalled = true;L.d("Leak installed.");return true;}
}

2、采集LeakCanary信息

LeakCanary初始化主要参考LeakCanary文档,当发生内存泄漏的时候,将当前的内存信息封装后发送出去

public class GodEyePluginLeakCanary {@Keeppublic static void install(final Application application, final Leak leakModule) {ThreadUtil.sMain.execute(new Runnable() {@Overridepublic void run() {AppWatcher.INSTANCE.manualInstall(application);LeakCanary.INSTANCE.showLeakDisplayActivityLauncherIcon(false);LeakCanary.setConfig(new LeakCanary.Config().newBuilder().requestWriteExternalStoragePermission(false).dumpHeap(true).onHeapAnalyzedListener(new OnHeapAnalyzedListener() {@Overridepublic void onHeapAnalyzed(@NotNull HeapAnalysis heapAnalysis) {if (heapAnalysis instanceof HeapAnalysisFailure) {L.w("GodEyePluginLeakCanary leak analysis failure:" + heapAnalysis.toString());return;}if (!(heapAnalysis instanceof HeapAnalysisSuccess)) {L.w("GodEyePluginLeakCanary leak analysis type error: " + heapAnalysis.getClass().getName());return;}final HeapAnalysisSuccess analysisSuccess = (HeapAnalysisSuccess) heapAnalysis;IteratorUtil.forEach(analysisSuccess.getAllLeaks().iterator(), new Consumer<shark.Leak>() {@Overridepublic void accept(shark.Leak leak) {leakModule.produce(new LeakInfo(analysisSuccess.getCreatedAtTimeMillis(), analysisSuccess.getAnalysisDurationMillis(), leak));}});}}).build());AppWatcher.setConfig(new AppWatcher.Config().newBuilder().enabled(true).build());}});}
}

3、总结

原理同样很简单,借助当前的第三方的优秀框架,通过热插拔的形式,引入到我们的性能监控中

5.3 NetWork(OkHttp)

NetWork的监控主要是计算网络的请求时间,通过OkHttp提供的拦截器中,有对应的回调

源码分析

1、启动NetWork的监控

NetWork的监控通过OkHttp自定义拦截器和回调去实现,项目的网络请求,就由当前初始化的这个OkHttp客户端去请求,即可达到监听的效果

  • eventListenerFactory:表示OkHttp监听请求到结束所有过程的监听器
  • addNetworkInterceptor:表示设置有网络时候的拦截器
GodEyePluginOkNetwork godEyePluginOkNetwork = new GodEyePluginOkNetwork();
mZygote = new OkHttpClient.Builder().eventListenerFactory(godEyePluginOkNetwork).addNetworkInterceptor(godEyePluginOkNetwork).build();

2、采集NetWork信息

采集网络信息都在拦截器和监听器里面

public class GodEyePluginOkNetwork extends OkHttpNetworkContentInterceptor implements EventListener.Factory {public GodEyePluginOkNetwork() {super(new HttpContentTimeMapping());}@Overridepublic EventListener create(Call call) {return new OkNetworkEventListener(this.mHttpContentTimeMapping);}
}

由于OkHttp封装得很好,所有的信息回调都会有,这里只是做时间的记录和一些关键的信息的记录

  • RequestHeader时间
  • RequestBody时间
  • ResponseHeader时间
  • ResponseBody时间
  • Connect时间
  • Dns时间
  • Other时间
class OkNetworkEventListener extends EventListener {private NetworkInfo<HttpContent> mNetworkInfo;private long mCallStartTimeMillis;private long mDnsStartTimeMillis;private long mConnectionStartTimeMillis;private long mRequestHeadersStartTimeMillis;private long mRequestBodyStartTimeMillis;private long mResponseHeadersStartTimeMillis;private long mResponseBodyStartTimeMillis;private HttpContentTimeMapping mHttpContentTimeMapping;OkNetworkEventListener(HttpContentTimeMapping httpContentTimeMapping) {this.mHttpContentTimeMapping = httpContentTimeMapping;this.mNetworkInfo = new NetworkInfo<>();this.mNetworkInfo.networkTime = new NetworkTime();this.mNetworkInfo.extraInfo = new HashMap<>();}@Overridepublic void callStart(Call call) {super.callStart(call);mCallStartTimeMillis = System.currentTimeMillis();this.mNetworkInfo.summary = call.request().method() + " " + call.request().url();}@Overridepublic void dnsStart(Call call, String domainName) {super.dnsStart(call, domainName);mDnsStartTimeMillis = System.currentTimeMillis();}@Overridepublic void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {super.dnsEnd(call, domainName, inetAddressList);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("DnsTime", System.currentTimeMillis() - mDnsStartTimeMillis);}@Overridepublic void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {super.connectStart(call, inetSocketAddress, proxy);mConnectionStartTimeMillis = System.currentTimeMillis();}@Overridepublic void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {super.connectEnd(call, inetSocketAddress, proxy, protocol);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("ConnectTime", System.currentTimeMillis() - mConnectionStartTimeMillis);}@Overridepublic void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol,IOException ioe) {super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("ConnectTime", System.currentTimeMillis() - mConnectionStartTimeMillis);}@Overridepublic void connectionAcquired(Call call, Connection connection) {super.connectionAcquired(call, connection);Handshake handshake = connection.handshake();String cipherSuite = "";String tlsVersion = "";if (handshake != null) {cipherSuite = handshake.cipherSuite().javaName();tlsVersion = handshake.tlsVersion().javaName();}Socket socket = connection.socket();int localPort = socket.getLocalPort();int remotePort = socket.getPort();String localIp = "";String remoteIp = "";InetAddress localAddress = socket.getLocalAddress();if (localAddress != null) {localIp = localAddress.getHostAddress();}InetAddress remoteAddress = socket.getInetAddress();if (remoteAddress != null) {remoteIp = remoteAddress.getHostAddress();}mNetworkInfo.extraInfo.put("cipherSuite", cipherSuite);mNetworkInfo.extraInfo.put("tlsVersion", tlsVersion);mNetworkInfo.extraInfo.put("localIp", localIp);mNetworkInfo.extraInfo.put("localPort", localPort);mNetworkInfo.extraInfo.put("remoteIp", remoteIp);mNetworkInfo.extraInfo.put("remotePort", remotePort);}@Overridepublic void requestHeadersStart(Call call) {super.requestHeadersStart(call);mRequestHeadersStartTimeMillis = System.currentTimeMillis();}@Overridepublic void requestHeadersEnd(Call call, Request request) {super.requestHeadersEnd(call, request);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("RequestHeadersTime", System.currentTimeMillis() - mRequestHeadersStartTimeMillis);}@Overridepublic void requestBodyStart(Call call) {super.requestBodyStart(call);mRequestBodyStartTimeMillis = System.currentTimeMillis();}@Overridepublic void requestBodyEnd(Call call, long byteCount) {super.requestBodyEnd(call, byteCount);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("RequestBodyTime", System.currentTimeMillis() - mRequestBodyStartTimeMillis);}@Overridepublic void responseHeadersStart(Call call) {super.responseHeadersStart(call);mResponseHeadersStartTimeMillis = System.currentTimeMillis();}@Overridepublic void responseHeadersEnd(Call call, Response response) {super.responseHeadersEnd(call, response);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("ResponseHeadersTime", System.currentTimeMillis() - mResponseHeadersStartTimeMillis);}@Overridepublic void responseBodyStart(Call call) {super.responseBodyStart(call);mResponseBodyStartTimeMillis = System.currentTimeMillis();}@Overridepublic void responseBodyEnd(Call call, long byteCount) {super.responseBodyEnd(call, byteCount);this.mNetworkInfo.networkTime.networkTimeMillisMap.put("ResponseBodyTime", System.currentTimeMillis() - mResponseBodyStartTimeMillis);}@Overridepublic void callEnd(Call call) {super.callEnd(call);this.mNetworkInfo.networkTime.totalTimeMillis = System.currentTimeMillis() - mCallStartTimeMillis;mNetworkInfo.networkContent = mHttpContentTimeMapping.removeAndGetRecord(call);if (mNetworkInfo.networkContent != null) {if (mNetworkInfo.networkContent.httpResponse != null) {mNetworkInfo.isSuccessful = isSuccessful(mNetworkInfo.networkContent.httpResponse.code);}if (mNetworkInfo.networkContent.httpResponse != null) {mNetworkInfo.message = mNetworkInfo.networkContent.httpResponse.message;}}try {GodEyeHelper.onNetworkEnd(mNetworkInfo);} catch (UninstallException e) {e.printStackTrace();}}@Overridepublic void callFailed(Call call, IOException ioe) {super.callFailed(call, ioe);this.mNetworkInfo.networkTime.totalTimeMillis = System.currentTimeMillis() - mCallStartTimeMillis;mNetworkInfo.isSuccessful = false;mNetworkInfo.message = String.valueOf(ioe);mNetworkInfo.networkContent = mHttpContentTimeMapping.removeAndGetRecord(call);try {GodEyeHelper.onNetworkEnd(mNetworkInfo);} catch (UninstallException e) {e.printStackTrace();}}private static boolean isSuccessful(int code) {return code >= 200 && code < 300;}
}

最后通过GodEyeHelper.onNetworkEnd(mNetworkInfo);将数据发送出去

public static void onNetworkEnd(NetworkInfo networkInfo) throws UninstallException {GodEye.instance().<Network>getModule(GodEye.ModuleName.NETWORK).produce(networkInfo);L.d("GodEyeHelper onNetworkEnd: %s", networkInfo == null ? "null" : networkInfo.toSummaryString());
}

3、总结

原理同样很简单,借助当前的第三方的优秀框架提供的回调参数,通过热插拔的形式,引入到我们的性能监控中

5.4 MethodCanary

MethodCanary用的是作者的另一个框架,通过调用开始和结束,采集当前时间段内线程中执行的函数和时间线

源码分析

1、启动MethodCanary的监控

MethodCanary的启动没做任何事情,因为其采集是需要手动调用才可以

public class MethodCanary extends ProduceableSubject<MethodsRecordInfo> implements Install<MethodCanaryConfig> {private boolean mInstalled = false;private MethodCanaryConfig mMethodCanaryContext;@Overridepublic synchronized boolean install(final MethodCanaryConfig methodCanaryContext) {if (this.mInstalled) {L.d("MethodCanary already installed, ignore.");return true;}this.mMethodCanaryContext = methodCanaryContext;this.mInstalled = true;L.d("MethodCanary installed.");return true;}
}

2、采集MethodCanary信息

采集的过程使用MethodCanary提供的Api,需要手动调用启动和结束,结束后将返回的方法信息发送出去

public synchronized void startMonitor(String tag) {try {if (!isInstalled()) {L.d("MethodCanary start monitor fail, not installed.");return;}cn.hikyson.methodcanary.lib.MethodCanary.get().startMethodTracing(tag);L.d("MethodCanary start monitor success.");} catch (Exception e) {L.d("MethodCanary start monitor fail:" + e);}
}public synchronized void stopMonitor(String tag) {try {if (!isInstalled()) {L.d("MethodCanary stop monitor fail, not installed.");return;}cn.hikyson.methodcanary.lib.MethodCanary.get().stopMethodTracing(tag, new cn.hikyson.methodcanary.lib.MethodCanaryConfig(this.mMethodCanaryContext.lowCostMethodThresholdMillis()), (sessionTag, startMillis, stopMillis, methodEventMap) -> {long start0 = System.currentTimeMillis();MethodsRecordInfo methodsRecordInfo = MethodCanaryConverter.convertToMethodsRecordInfo(startMillis, stopMillis, methodEventMap);
//                        recordToFile(methodEventMap, methodsRecordInfo);long start1 = System.currentTimeMillis();MethodCanaryConverter.filter(methodsRecordInfo, this.mMethodCanaryContext);long end = System.currentTimeMillis();L.d(String.format("MethodCanary output success! cost %s ms, filter cost %s ms", end - start0, end - start1));produce(methodsRecordInfo);});L.d("MethodCanary stopped monitor and output processing...");} catch (Exception e) {L.d("MethodCanary stop monitor fail:" + e);}
}

作者也是通过Web点击开始和结束操作当前的MethodCanary

public class WebSocketMethodCanaryProcessor implements WebSocketProcessor {@Overridepublic void process(WebSocket webSocket, JSONObject msgJSONObject) {try {if ("start".equals(msgJSONObject.optString("payload"))) {GodEyeHelper.startMethodCanaryRecording("AndroidGodEye-Monitor-Tag");} else if ("stop".equals(msgJSONObject.optString("payload"))) {GodEyeHelper.stopMethodCanaryRecording("AndroidGodEye-Monitor-Tag");}webSocket.send(new ServerMessage("methodCanaryMonitorState", Collections.singletonMap("isRunning", GodEyeHelper.isMethodCanaryRecording("AndroidGodEye-Monitor-Tag"))).toString());} catch (UninstallException e) {L.e(String.valueOf(e));}}
}

3、总结

MethodCanary主要是依赖于作者的第三方库,通过框架的回调,获取我们想要的函数信息

5章 性能平台GodEye源码分析-第三方模块相关推荐

  1. 字节跳动Android三面视频解析:framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等

    前言 对于字节跳动的二面三面而言,Framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等问题都成高频问点!然而很多的朋友在面试时却答不上或者答不全!今天在这分享下这些 ...

  2. Python3.5源码分析-sys模块及site模块导入

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的sys模块初始化 根据分析完成builtins ...

  3. koa源码分析-co模块以及thunk

    Thunk以及CO模块 co4.0之前都是返回的thunk函数 之后的都是返回promise thunk thunk:在 JavaScript 语言中,Thunk 函数替换的是将多参数函数,替换成单参 ...

  4. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  5. Fabric源码分析-共识模块

    正好这些天要有一个需求要帮客户魔改Fabric-v0.6,把一些hyperchain的高级特性移植过去,借此机会把之前看过的源码在梳理一下. 下面就是对Fabric共识模块的源码分析和梳理,代码都是以 ...

  6. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

  7. Canal源码分析deployer模块

    canal有两种使用方式:1.独立部署 2.内嵌到应用中. deployer模块主要用于独立部署canal server.关于这两种方式的区别,请参见server模块源码分析.deployer模块源码 ...

  8. Zepto源码分析-event模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  9. WebRTC源码分析——Call模块

    目录 1. 引言 2. Call对象的创建 2.1 创建CallFactory对象 2.2 创建Call对象 2.2.1 PeerConnection.CreateCall_w 2.2.2 CallF ...

最新文章

  1. WSDL、SOAP、UDDI
  2. 同一账户同时只能在一处登陆(单点登陆)
  3. 使用img.src跨域请求
  4. 坚式计算机在线,小学数学竖式计算器
  5. html for 循环模板
  6. 通过webbrowser控件获取验证码
  7. linux交换分区的文件格式为,LINUX的交换分区或交换文件SWAP的查看与维护
  8. MyBatis中in的使用
  9. wordpress模板-单栏多色调全站pajx个人博客模板 idevs主题
  10. 重写equals()时为什么也得重写hashCode()之深度解读equals方法与hashCode方法渊源
  11. delete语句与reference约束冲突怎么解决_一条简单的更新语句,MySQL是如何加锁的?...
  12. PHP中self和static的区别,php面向对象程序设计中self与static的区别分析
  13. 数学与计算机专业的论文,数学与计算机论文
  14. 技术领导者空降后,如何管理全新的团队
  15. 极限-反函数极限问题
  16. 用友致远A6协同管理软件操作手册
  17. Windows之cmd命令检查网络
  18. C++程序设计(六)—— 继承和派生
  19. App Store拒绝原因
  20. 淘宝官方商品、交易、订单、物流接口列表

热门文章

  1. python输出到txt文件太大的原因_如何减小Python创建的txt文件的大小?
  2. Linux零基础作业,Linux作业1--基础20题
  3. 分享DotNetBar控件制作office 2007风格界面的视频教程(winform office 2007 风格)
  4. Mysql如何升级到5.7
  5. 2016 百度之星 B题(java实现)
  6. 二维码图片扫描 绘制扫描的图片的边框(Swift)
  7. 中国独立游戏节(IGF China)2010 全入围作品视频介绍
  8. 眼动数据分析基础_01
  9. PBR中BRDF常用的各类法线分布函数、几何函数总结(unity)
  10. java系统对接方案_阿里巴巴开放平台SDK(java版). 接入授权店铺数据,对接企业内部管理系统的解决方案...