█ 【安卓学习之第三方库】 Tencent Matrix使用-ANR
█ 系列文章目录
提示:这里是收集了安卓学习之常见问题的相关文章
- 【安卓学习之MP3】 MP3读取格式
- 【安卓学习之第三方库】 ZlwAudioRecorder学习:内部流程
- 【安卓学习之开源项目】 ParrotTongue:文字转语音(含LeLeTextToVoice、TextToMp3项目)
█ 文章目录
█ 读前说明
- 本文通过学习别人写demo,学习一些课件,参考一些博客,’学习相关知识,如果涉及侵权请告知
- 本文只简单罗列相关的代码实现过程
- 涉及到的逻辑以及说明也只是简单介绍,主要当做笔记,了解过程而已
- Android 微信 APM工具 Matrix-android
█ Matrix-android、bugly、leakcanary
- Canary
Canary(金丝雀) Bleeding Edge(一种最新的、因而也并非完美的技术),大约周更。这是最早被发布的预览版本,
Stable | 稳定版/正式版 | 适合追求稳定的普通用户使用。 |
---|---|---|
beta | 测试版 | 适合喜欢较稳定又可尝鲜的朋友下载使用。该版本是新的正式版发布前的公开测试所用,版本上的新功能不会作太多修改,主要为安全上的测试,但可能会存在不稳定情况。 |
dev | 开发版 | 适合开发人员使用。主要为功能上的测试,可能存在稳定性问题,通常更新速度为一周一次。 |
canary | 金丝雀版本 | 仅适合开发人员或追求最新版本的用户使用。2010年7月加入,比开发版更新快但更不稳定,相对的也会加入更多测试性的新功能。 |
- Matrix-android
功能 | 收集崩溃、ANR、卡顿和爆内存数据 |
---|---|
监控范围 | 应用安装包大小,帧率变化,启动耗时,卡顿,慢方法,SQLite 操作优化,文件读写,内存泄漏等等 |
示例 | samples/sample-android/app |
注意 | 由于 JCenter 服务将于 2022 年 2 月 1 日下线,我们已将 Matrix 新版本(>= 0.8.0) maven repo 发布至 MavenCentral。 |
Matrix 各模块
基础包 | matrix-android-lib | 初始化监听等,含Matrix、Matrix.Builder、Issue、 IDynamicConfig、DefaultPluginListener、MatrixLog |
---|---|---|
- | matrix-android-commons | 目前没用 |
Resource Canary | matrix-resource-canary-android matrix-resource-canary-common | 便于在不打断自动化测试的前提下持续输出分析后的检测结果,检测重复 Bitmap 对象 |
Trace Canary | matrix-trace-canary | 准确监控ANR ,保存系统产生的ANR Trace文件 |
SQLite Lint | matrix-sqlite-lint-android-sdk | 把控SQLite质量 |
IO Canary | matrix-io-canary | IO 性能、泄漏全面监控 |
Battery Canary | matrix-battery-canary | 耗电量优化 |
Memory Hook Pthread Hook WVPreAllocHook | matrix-hooks | 内存泄漏 线程泄漏\native 线程栈空间 WebView内存分配 |
APK Checker | matrix-apk-canary-2.0.0.jar | 追踪和对比每个 APK 版本之间的变化 |
Backtrace Component | - | 快速回溯 native 调用栈的 backtrace 组件 |
-
bugly
异常上报,应用集成SDK后,即可在Web站点查看应用上报的崩溃数据和联网数据。
缺点:android6.0以后的设备,无法上传 ANR日志
-
leakcanary
AndroidStudio自带的Android Profiler、MAT等工具可以进行内存分析;手机端也有类似的内存分析app:leakcanary
leakCanary是Square开源框架,是一个Android和Java的内存泄露检测库,,集成sdk后,就会生成一个辅助应用,名字叫Leaks,
当Leakcanary检测到某个 activity 有内存泄露,会以通知栏形式报出,打开Leaks,就能看到具体函数以及代码行数
优点 |
---|
针对Android Activity组件完全自动化的内存泄漏检查 |
可定制一些行为(dump文件和leaktrace对象的数量、自定义例外、分析结果的自定义处理等) |
集成到自己工程并使用的成本很低 |
友好的界面展示和通知 |
缺点 |
---|
不能进行ANR检测,不过有另一款类似的app:BlockCanary |
█ Matrix-android 的使用
- 根目录下的build.gradle文件中添加依赖:
buildscript {
ext {
MATRIX_VERSION='2.0.0'
}
repositories {
google()
maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath ("com.tencent.matrix:matrix-gradle-plugin:${MATRIX_VERSION}")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
}
- APP目录下的build.gradle中添加依赖
dependencies {
implementation "com.tencent.matrix:matrix-android-lib:${MATRIX_VERSION}"
implementation "com.tencent.matrix:matrix-trace-canary:${MATRIX_VERSION}"
}
apply plugin: 'com.tencent.matrix-plugin'
matrix {
trace {
enable = true //if you don't want to use trace canary, set false
baseMethodMapFile = "${project.buildDir}/matrix_output/Debug.methodmap"
blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"
}
}
- Application中初始化
@Override
public void onCreate() {
super.onCreate();
Matrix.Builder builder = new Matrix.Builder(this); // build matrix
builder.pluginListener(new TestPluginListener(this)); // add general pluginListener
DynamicConfigImplDemo dynamicConfig = new DynamicConfigImplDemo(); // dynamic config
//trace
TraceConfig traceConfig = new TraceConfig.Builder()
.dynamicConfig(dynamicConfig)
//按照自己需求开启以下监控任务
.enableFPS(dynamicConfig.isFPSEnable())// true
.enableEvilMethodTrace(dynamicConfig.isTraceEnable())// true
.enableAnrTrace(dynamicConfig.isTraceEnable())// true
.enableStartup(dynamicConfig.isTraceEnable())// true
//一定要写,改成自己项目中的splash页面即可,不然会奔溃
.splashActivities("com.bx.bxanr.MainActivity;")
//debug模式
.isDebug(true)
//dev环境
.isDevEnv(false)
.build();
TracePlugin tracePlugin = new TracePlugin(traceConfig);
builder.plugin(tracePlugin);
//init matrix
Matrix.init(builder.build());
// start plugin
tracePlugin.start();
}
- ANR监听事件 TestPluginListener:
public class TestPluginListener extends DefaultPluginListener {
public static final String TAG = "TestPluginListener99";
public SoftReference<Context> softReference;
private final Handler mHandler = new Handler(Looper.getMainLooper());
public TestPluginListener(Context context) {
super(context);
softReference = new SoftReference<>(context);
}
@Override
public void onReportIssue(Issue issue) {
super.onReportIssue(issue);
MatrixLog.e(TAG, issue.toString());
Log.e(TAG, issue.toString());
// IssuesMap.put(IssueFilter.getCurrentFilter(), issue);
mHandler.post(new Runnable() {// 等ui线程阻塞结束后,才执行
@Override
public void run() {
Context context = softReference.get();
String message = String.format("666 Report an issue - [%s]. context - [%s]", issue.getTag(),context);
Log.e(TAG, message);
if (context != null) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
}
});
// jumpToIssueActivity();
}
}
- 生成一个ANR事件:
findViewById(R.id.test_anr_ImageButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testANR();
}
});
protected void testANR() {
try {
var1 = 0;
while (var1++ < 15) {
Log.e("TestPluginListener", "try main sleep for make a test anr! try:" + var1 + "/15 , kill it if you don't want to wait!");
try {
Thread.sleep(5000L);
} catch (InterruptedException var2) {
var2.printStackTrace();
Log.e("TestPluginListener22", "Throwable:" + var2.getMessage());
}
}
} catch (Throwable var2) {
var2.printStackTrace();
Log.e("TestPluginListener33", "Throwable:" + var2.getMessage());
}
}
- 打印的日志信息:
可以看出是【at com.bx.bxanr.MainActivity:testANR(39)】出问题
//## 阻塞50s
E/TestPluginListener: try main sleep for make a test anr! try:1/10 , kill it if you don't want to wait!
I/Matrix.DeviceUtil: getTotalMemory cost:4, total_mem:2987102208, LowMemoryThresold:226492416, Memory Class:192
I/Matrix.DeviceUtil: [getLevel] totalMemory:2987102208 coresNum:8
I/Matrix.DeviceUtil: getLevel, cost:15, level:MIDDLE
I/default_matrix_: type=1400 audit(0.0:2919): avc: denied { read } for name="stat" dev="proc" ino=4026532302 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:proc_stat:s0 tclass=file permissive=1
I/default_matrix_: type=1400 audit(0.0:2920): avc: denied { open } for path="/proc/stat" dev="proc" ino=4026532302 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:proc_stat:s0 tclass=file permissive=1
I/Matrix.DeviceUtil: getAppCpuRate cost:33,rate:8.774502021974309E-4
I/Matrix.DefaultPluginListener: report issue content: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":8.774502021974309E-4,"mem":2987102208,"mem_free":1448560,"detail":"LAG","scene":"com.bx.bxanr.MainActivity","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","isProcessForeground":true,"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828350594}]
//## 监听到ANR
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":8.774502021974309E-4,"mem":2987102208,"mem_free":1448560,"detail":"LAG","scene":"com.bx.bxanr.MainActivity","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","isProcessForeground":true,"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828350594}]
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":8.774502021974309E-4,"mem":2987102208,"mem_free":1448560,"detail":"LAG","scene":"com.bx.bxanr.MainActivity","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","isProcessForeground":true,"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828350594}]
E/Matrix.AnrTracer: happens lag : {"machine":"MIDDLE","cpu_app":8.774502021974309E-4,"mem":2987102208,"mem_free":1448560,"detail":"LAG","scene":"com.bx.bxanr.MainActivity","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","isProcessForeground":true,"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828350594}
E/TestPluginListener: try main sleep for make a test anr! try:2/10 , kill it if you don't want to wait!
I/Matrix.AppMethodBeat: [copyData] [14152:14161] length:10 cost:0ms
W/Matrix.TraceDataUtils: [structuredDataToStack] has never out method[2], isIn:true, inTime:26295280, endTime:26300284,rawData size:3
W/Matrix.TraceDataUtils: [structuredDataToStack] has never out method[2], isIn:true, inTime:26295280, endTime:26300284,rawData size:2
W/Matrix.TraceDataUtils: [structuredDataToStack] has never out method[7308], isIn:true, inTime:26295280, endTime:26300284,rawData size:1
W/Matrix.TraceDataUtils: [structuredDataToStack] has never out method[1048574], isIn:true, inTime:26295280, endTime:26300284,rawData size:0
I/Matrix.TraceDataUtils: stackToTree: count=7
W/Matrix.AnrTracer: -
>>>>>>>>>>>>>>>>>>>>>>> maybe happens ANR(5004 ms)! <<<<<<<<<<<<<<<<<<<<<<<
|* [Status]
|* Scene: com.bx.bxanr.MainActivity
|* Foreground: true
|* Priority: 10 Nice: -10
|* is64BitRuntime: true
|* [Memory]
|* DalvikHeap: 10858kb
|* NativeHeap: 10365kb
|* VmSize: 4351716kb
|* [doFrame]
|* inputCost:animationCost:traversalCost
|* 0:0:0
|* [Thread]
|* Stack(TIMED_WAITING):
|* at com.bx.bxanr.MainActivity:testANR(39)
|* at com.bx.bxanr.MainActivity$2:onClick(25)
|* at android.view.View:performClick(6294)
|* at com.google.android.material.button.MaterialButton:performClick(1119)
|* at android.view.View$PerformClick:run(24774)
|* at android.os.Handler:handleCallback(790)
|* at android.os.Handler:dispatchMessage(99)
|* at android.os.Looper:loop(164)
|* at android.app.ActivityThread:main(6518)
|* [Trace]
|* StackKey: 2|
|* TraceStack:
|* [id count cost]
|* 1048574 1 5004
|* .7308 1 5004
|* ..7306 1 0
|* ...7305 1 0
|* ....7309 1 0
|* ..2 1 5004
|* ...2 1 5004
=========================================================================
postTime:26295280 curTime:26300284
I/Matrix.DeviceUtil: getAppCpuRate cost:20,rate:9.488731868654589E-4
I/Matrix.DefaultPluginListener: report issue content: tag[Trace_EvilMethod]type[0];key[26295280334291];content[{"machine":"MIDDLE","cpu_app":9.488731868654589E-4,"mem":2987102208,"mem_free":1446552,"detail":"ANR","cost":5004,"stackKey":"2|","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,5004\n1,7308,1,5004\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,5004\n3,2,1,5004\n","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","processPriority":10,"processNice":-10,"isProcessForeground":true,"memory":{"dalvik_heap":10858,"native_heap":10365,"vm_size":4351716},"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828353601}]
//## 再次监听到ANR
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[26295280334291];content[{"machine":"MIDDLE","cpu_app":9.488731868654589E-4,"mem":2987102208,"mem_free":1446552,"detail":"ANR","cost":5004,"stackKey":"2|","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,5004\n1,7308,1,5004\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,5004\n3,2,1,5004\n","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","processPriority":10,"processNice":-10,"isProcessForeground":true,"memory":{"dalvik_heap":10858,"native_heap":10365,"vm_size":4351716},"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828353601}]
//## 再次监听到ANR
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[26295280334291];content[{"machine":"MIDDLE","cpu_app":9.488731868654589E-4,"mem":2987102208,"mem_free":1446552,"detail":"ANR","cost":5004,"stackKey":"2|","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,5004\n1,7308,1,5004\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,5004\n3,2,1,5004\n","threadStack":" \nat com.bx.bxanr.MainActivity:testANR(39)\nat com.bx.bxanr.MainActivity$2:onClick(25)\nat android.view.View:performClick(6294)\nat com.google.android.material.button.MaterialButton:performClick(1119)\nat android.view.View$PerformClick:run(24774)\nat android.os.Handler:handleCallback(790)\nat android.os.Handler:dispatchMessage(99)\nat android.os.Looper:loop(164)\nat android.app.ActivityThread:main(6518)\n","processPriority":10,"processNice":-10,"isProcessForeground":true,"memory":{"dalvik_heap":10858,"native_heap":10365,"vm_size":4351716},"tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828353601}]
E/TestPluginListener: try main sleep for make a test anr! try:3/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:4/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:5/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:6/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:7/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:8/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:9/10 , kill it if you don't want to wait!
E/TestPluginListener: try main sleep for make a test anr! try:10/10 , kill it if you don't want to wait!
I/Matrix.AppMethodBeat: [copyData] [14152:14165] length:14 cost:0ms
I/Matrix.TraceDataUtils: stackToTree: count=7
I/Choreographer: Skipped 3001 frames! The application may be doing too much work on its main thread.
W/Matrix.EvilMethodTracer: -
>>>>>>>>>>>>>>>>>>>>> maybe happens Jankiness!(50010ms) <<<<<<<<<<<<<<<<<<<<<
|* [Status]
|* Scene: com.bx.bxanr.MainActivity
|* Foreground: true
|* Priority: 10 Nice: -10
|* is64BitRuntime: true
|* CPU: 0.01%
|* [doFrame]
|* inputCost:animationCost:traversalCost
|* 3355573:1188177:3037395
|* StackKey: 2|
|* TraceStack:
|* [id count cost]
|* 1048574 1 50011
|* .7308 1 50007
|* ..7306 1 0
|* ...7305 1 0
|* ....7309 1 0
|* ..2 1 50007
|* ...2 1 50007
=========================================================================
I/Matrix.DeviceUtil: getAppCpuRate cost:20,rate:0.0012852167232400029
//## ANR等待结束,执行ui线程的Toast.makeText(context, message, Toast.LENGTH_LONG).show();
E/TestPluginListener99: 666 Report an issue - [Trace_EvilMethod]. context - [com.bx.bxanr.BxANRApp@9751672]
I/Matrix.DefaultPluginListener: report issue content: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":0.0012852167232400029,"mem":2987102208,"mem_free":1451928,"detail":"NORMAL","cost":50011,"usage":"0.01%","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,50011\n1,7308,1,50007\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,50007\n3,2,1,50007\n","stackKey":"2|","tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828398594}]
//## 再次监听到ANR
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":0.0012852167232400029,"mem":2987102208,"mem_free":1451928,"detail":"NORMAL","cost":50011,"usage":"0.01%","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,50011\n1,7308,1,50007\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,50007\n3,2,1,50007\n","stackKey":"2|","tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828398594}]
E/TestPluginListener99: tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":0.0012852167232400029,"mem":2987102208,"mem_free":1451928,"detail":"NORMAL","cost":50011,"usage":"0.01%","scene":"com.bx.bxanr.MainActivity","stack":"0,1048574,1,50011\n1,7308,1,50007\n2,7306,1,0\n3,7305,1,0\n4,7309,1,0\n2,2,1,50007\n3,2,1,50007\n","stackKey":"2|","tag":"Trace_EvilMethod","process":"com.bx.bxanr","time":1630828398594}]
//## ANR等待结束,执行ui线程的Toast.makeText(context, message, Toast.LENGTH_LONG).show();
E/TestPluginListener99: 666 Report an issue - [Trace_EvilMethod]. context - [com.bx.bxanr.BxANRApp@9751672]
E/TestPluginListener99: 666 Report an issue - [Trace_EvilMethod]. context - [com.bx.bxanr.BxANRApp@9751672]
█ 遇到的问题
█ 相关资料
提示:这里是参考的相关文章
- 官方项目地址:Matrix-android
- Android SDK 使用指南 - Bugly 文档
- 2019.05.10 Android 微信APM工具 Matrix使用 - 简书
- 2020-12-31 使用bugly没有ANR上报? - 问答 - 云+社区 - 腾讯云:一加6T,Android 9,抓不到ANR,
- 重点:2019.04.30 Android CRASH ANR 日志收集 - 简书:Android6.0后的设备没系统权限,无法上传ANR信息(/data/system/dropbox/trace.txt)
- 2019.10.29 APP测试之CRASH|| ANR - 简书:举例空指针、 数组越界、内存溢出、非UI线程操作UI线程、类型转换、遍历数组的时候不能进行增删改操作、数据库关闭的状态下,强行打开、ANR的产生原因等
- 2017-06-08【Android端ANR卡顿检测】BlockCanary检测 - 可可_小虾米 - 博客园
█ 免责声明
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持! |
---|
提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/120112498