【安卓学习之第三方库】 Tencent Matrix-android使用-ANR

█ 【安卓学习之第三方库】 Tencent Matrix使用-ANR


█ 系列文章目录

提示:这里是收集了安卓学习之常见问题的相关文章


█ 文章目录


█ 读前说明

  • 本文通过学习别人写demo,学习一些课件,参考一些博客,’学习相关知识,如果涉及侵权请告知
  • 本文只简单罗列相关的代码实现过程
  • 涉及到的逻辑以及说明也只是简单介绍,主要当做笔记,了解过程而已
  • Android 微信 APM工具 Matrix-android

█ Matrix-android、bugly、leakcanary

  1. Canary

Canary(金丝雀) Bleeding Edge(一种最新的、因而也并非完美的技术),大约周更。这是最早被发布的预览版本,

Stable稳定版/正式版适合追求稳定的普通用户使用。
beta测试版适合喜欢较稳定又可尝鲜的朋友下载使用。该版本是新的正式版发布前的公开测试所用,版本上的新功能不会作太多修改,主要为安全上的测试,但可能会存在不稳定情况。
dev开发版适合开发人员使用。主要为功能上的测试,可能存在稳定性问题,通常更新速度为一周一次。
canary金丝雀版本仅适合开发人员或追求最新版本的用户使用。2010年7月加入,比开发版更新快但更不稳定,相对的也会加入更多测试性的新功能。
  1. 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 Canarymatrix-resource-canary-android
matrix-resource-canary-common
便于在不打断自动化测试的前提下持续输出分析后的检测结果,检测重复 Bitmap 对象
Trace Canarymatrix-trace-canary准确监控ANR ,保存系统产生的ANR Trace文件
SQLite Lintmatrix-sqlite-lint-android-sdk把控SQLite质量
IO Canarymatrix-io-canaryIO 性能、泄漏全面监控
Battery Canarymatrix-battery-canary耗电量优化
Memory Hook
Pthread Hook
WVPreAllocHook
matrix-hooks内存泄漏
线程泄漏\native 线程栈空间
WebView内存分配
APK Checkermatrix-apk-canary-2.0.0.jar追踪和对比每个 APK 版本之间的变化
Backtrace Component-快速回溯 native 调用栈的 backtrace 组件
  1. bugly
    异常上报,应用集成SDK后,即可在Web站点查看应用上报的崩溃数据和联网数据。
    缺点:android6.0以后的设备,无法上传 ANR日志
    在这里插入图片描述

  2. 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 的使用

  1. 根目录下的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/'}
    }
}
  1. 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"
    }
}

  1. 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();
    }
  1. 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();
    }

}
  1. 生成一个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());
        }
    }
  1. 打印的日志信息:
    可以看出是【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]

█ 遇到的问题


█ 相关资料

提示:这里是参考的相关文章

  1. 官方项目地址:Matrix-android
  2. Android SDK 使用指南 - Bugly 文档
  3. 2019.05.10 Android 微信APM工具 Matrix使用 - 简书
  4. 2020-12-31 使用bugly没有ANR上报? - 问答 - 云+社区 - 腾讯云:一加6T,Android 9,抓不到ANR,
  5. 重点:2019.04.30 Android CRASH ANR 日志收集 - 简书:Android6.0后的设备没系统权限,无法上传ANR信息(/data/system/dropbox/trace.txt)
  6. 2019.10.29 APP测试之CRASH|| ANR - 简书:举例空指针、 数组越界、内存溢出、非UI线程操作UI线程、类型转换、遍历数组的时候不能进行增删改操作、数据库关闭的状态下,强行打开、ANR的产生原因等
  7. 2017-06-08【Android端ANR卡顿检测】BlockCanary检测 - 可可_小虾米 - 博客园

█ 免责声明

博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!

提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/120112498

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值