HarmonyOS开发:崩溃分析——Crash分析

举报
Jack20 发表于 2026/06/25 20:51:21 2026/06/25
【摘要】 HarmonyOS开发:崩溃分析——Crash分析📌 核心要点:崩溃是用户体验的"致命伤"——CrashCollection集成崩溃采集、堆栈分析定位根因、崩溃率监控实时告警,让你在用户投诉之前就发现并修复崩溃。 背景与动机你正在吃午饭,手机突然震个不停。打开一看,用户群里炸了——“应用闪退了!”“刚下的单没了!”“什么破应用!”你赶紧打开后台,发现崩溃率从0.1%飙升到了5%。这意味着...

HarmonyOS开发:崩溃分析——Crash分析

📌 核心要点:崩溃是用户体验的"致命伤"——CrashCollection集成崩溃采集、堆栈分析定位根因、崩溃率监控实时告警,让你在用户投诉之前就发现并修复崩溃。

背景与动机

你正在吃午饭,手机突然震个不停。打开一看,用户群里炸了——“应用闪退了!”“刚下的单没了!”“什么破应用!”

你赶紧打开后台,发现崩溃率从0.1%飙升到了5%。这意味着每20个用户就有1个遇到崩溃。如果是100万日活的应用,那就是5万个用户在骂你。

更可怕的是——你不知道崩溃发生在哪。是哪个页面?哪个操作?哪种设备?你只能看用户描述猜,然后本地复现。运气好半小时能复现,运气不好三天都复现不了。

崩溃分析要解决的核心问题:

  • 崩溃了没:实时监控崩溃率,异常时立刻告警
  • 崩在哪了:崩溃堆栈、设备信息、操作路径,精确定位崩溃位置
  • 为什么崩:根因分析,是空指针?数组越界?内存溢出?
  • 怎么修:根据堆栈信息快速修复,灰度验证,全量发布

鸿蒙应用崩溃分析有其特殊性——HiAppEvent事件体系、FaultLogger日志系统、CrashCollection崩溃采集,这些是鸿蒙特有的崩溃分析基础设施。

核心原理

崩溃分析的完整链路:崩溃采集 → 日志上报 → 堆栈分析 → 根因定位 → 修复验证

flowchart TB
    subgraph 采集层
        A[CrashCollection<br/>崩溃信号捕获]
        B[FaultLogger<br/>系统故障日志]
        C[HiAppEvent<br/>应用事件打点]
    end
    
    subgraph 上报层
        A --> D[崩溃日志组装]
        B --> D
        C --> D
        D --> E[附加设备信息]
        E --> F[压缩加密上报]
    end
    
    subgraph 分析层
        F --> G[堆栈符号化]
        G --> H[崩溃归类聚合]
        H --> I[根因分析]
    end
    
    subgraph 处理层
        I --> J{崩溃等级}
        J -->|P0 致命| K[立即告警+热修复]
        J -->|P1 严重| L[当日修复+灰度发布]
        J -->|P2 一般| M[排期修复+常规发布]
        J -->|P3 轻微| N[记录跟踪+择机修复]
    end
    
    classDef collect fill:#E17055,stroke:#D63031,color:#fff
    classDef upload fill:#FDCB6E,stroke:#F0B429,color:#333
    classDef analyze fill:#74B9FF,stroke:#0984E3,color:#fff
    classDef handle fill:#55EFC4,stroke:#00B894,color:#333
    classDef decision fill:#A29BFE,stroke:#6C5CE7,color:#fff
    
    class A,B,C collect
    class D,E,F upload
    class G,H,I analyze
    class J decision
    class K,L,M,N handle

崩溃类型与处理策略:

崩溃类型 常见原因 处理优先级
JS Crash 空指针引用、类型错误、未捕获异常 P0
Native Crash 内存越界、野指针、栈溢出 P0
ANR 主线程阻塞、死锁、耗时操作 P1
OOM 内存泄漏、大对象未释放 P1
Watchdog 应用卡死、无响应 P2

代码实战

基础用法:CrashCollection集成

先把崩溃采集接进来,让它自动捕获崩溃。

// CrashCollector.ets - 崩溃采集管理
import { hiAppEvent } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 崩溃事件参数
export interface CrashInfo {
  crashType: string;        // 崩溃类型
  crashMessage: string;     // 崩溃消息
  crashStack: string;       // 崩溃堆栈
  deviceModel: string;      // 设备型号
  systemVersion: string;    // 系统版本
  appVersion: string;       // 应用版本
  timestamp: number;        // 崩溃时间
  userId?: string;          // 用户ID
  lastPage?: string;        // 最后访问页面
  lastAction?: string;      // 最后操作
}

export class CrashCollector {
  private static instance: CrashCollector;
  private context: common.UIAbilityContext | null = null;
  private isInitialized = false;
  
  // 最近一次操作记录——崩溃时附加
  private lastPageName: string = '';
  private lastActionName: string = '';
  private currentUserId: string = '';
  
  static getInstance(): CrashCollector {
    if (!CrashCollector.instance) {
      CrashCollector.instance = new CrashCollector();
    }
    return CrashCollector.instance;
  }
  
  // 初始化崩溃采集
  init(context: common.UIAbilityContext): void {
    if (this.isInitialized) return;
    this.context = context;
    
    try {
      // 配置HiAppEvent
      hiAppEvent.configure({
        disable: false,           // 开启事件打点
        maxStorage: '10M',        // 最大存储空间
      });
      
      // 添加崩溃事件观察者
      hiAppEvent.on(hiAppEvent.EventType.FAULT, (events: hiAppEvent.AppEventInfo[]) => {
        this.handleCrashEvents(events);
      });
      
      this.isInitialized = true;
      console.info('[CrashCollector] 崩溃采集初始化完成');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[CrashCollector] 初始化失败: ${err.code} - ${err.message}`);
    }
  }
  
  // 处理崩溃事件
  private handleCrashEvents(events: hiAppEvent.AppEventInfo[]): void {
    for (const event of events) {
      console.error(`[CrashCollector] 捕获崩溃事件: ${event.name}`);
      console.error(`[CrashCollector] 崩溃类型: ${event.eventType}`);
      console.error(`[CrashCollector] 崩溃信息: ${JSON.stringify(event.params)}`);
      
      // 附加上下文信息
      const crashInfo: CrashInfo = {
        crashType: this.getCrashType(event),
        crashMessage: event.name,
        crashStack: JSON.stringify(event.params),
        deviceModel: this.getDeviceModel(),
        systemVersion: this.getSystemVersion(),
        appVersion: this.getAppVersion(),
        timestamp: Date.now(),
        userId: this.currentUserId,
        lastPage: this.lastPageName,
        lastAction: this.lastActionName,
      };
      
      // 上报崩溃信息到自己的服务端
      this.reportCrashToServer(crashInfo);
    }
  }
  
  // 记录当前页面——崩溃时可以知道用户在哪个页面
  recordPage(pageName: string): void {
    this.lastPageName = pageName;
  }
  
  // 记录当前操作——崩溃时可以知道用户在做什么
  recordAction(actionName: string): void {
    this.lastActionName = actionName;
  }
  
  // 设置用户ID
  setUserId(userId: string): void {
    this.currentUserId = userId;
  }
  
  // 手动上报错误——捕获到的非致命错误
  reportError(error: Error, context?: Record<string, Object>): void {
    const errorInfo = {
      error_name: error.name,
      error_message: error.message,
      error_stack: error.stack || '',
      ...context,
      timestamp: Date.now(),
      last_page: this.lastPageName,
      last_action: this.lastActionName,
    };
    
    try {
      hiAppEvent.write({
        domain: 'app_error',
        name: 'manual_error_report',
        eventType: hiAppEvent.EventType.FAULT,
        params: errorInfo,
      });
      console.info('[CrashCollector] 错误上报成功');
    } catch (err) {
      console.error('[CrashCollector] 错误上报失败');
    }
  }
  
  // ========== 私有方法 ==========
  
  private getCrashType(event: hiAppEvent.AppEventInfo): string {
    if (event.name.includes('JS_CRASH')) return 'JS_CRASH';
    if (event.name.includes('NATIVE_CRASH')) return 'NATIVE_CRASH';
    if (event.name.includes('APP_FREEZE')) return 'ANR';
    if (event.name.includes('OOM')) return 'OOM';
    return 'UNKNOWN';
  }
  
  private getDeviceModel(): string {
    // 获取设备型号
    return 'unknown';
  }
  
  private getSystemVersion(): string {
    // 获取系统版本
    return 'unknown';
  }
  
  private getAppVersion(): string {
    // 获取应用版本
    return '1.0.0';
  }
  
  // 上报崩溃到服务端
  private async reportCrashToServer(crashInfo: CrashInfo): Promise<void> {
    // 这里实现上报到自己的崩溃分析服务
    // 实际项目中用HTTP请求上报
    console.info('[CrashCollector] 崩溃信息已上报到服务端');
  }
}

在EntryAbility中初始化:

// EntryAbility.ets
import { CrashCollector } from '../crash/CrashCollector';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam): void {
    // 第一时间初始化崩溃采集
    CrashCollector.getInstance().init(this.context);
  }
}

进阶用法:崩溃堆栈分析与归类

崩溃采集上来了,但100个崩溃可能只有5种原因。你得归类聚合,才能高效修复。

// CrashAnalyzer.ets - 崩溃分析与归类
import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { CrashInfo } from './CrashCollector';

// 崩溃聚合结果
export interface CrashGroup {
  signature: string;        // 崩溃签名(用于聚合)
  crashType: string;        // 崩溃类型
  topFrame: string;         // 顶层调用帧
  count: number;            // 发生次数
  firstTime: number;        // 首次发生时间
  lastTime: number;         // 最近发生时间
  affectedUsers: Set<string>; // 受影响用户
  severity: CrashSeverity;  // 严重程度
  samples: CrashInfo[];     // 样本崩溃(最多保留10条)
}

// 崩溃严重程度
export enum CrashSeverity {
  CRITICAL = 'critical',    // P0:崩溃率>1%,影响大量用户
  HIGH = 'high',            // P1:崩溃率0.1%-1%,影响部分用户
  MEDIUM = 'medium',        // P2:崩溃率0.01%-0.1%,偶发
  LOW = 'low',              // P3:崩溃率<0.01%,极少发生
}

// 崩溃率统计
export interface CrashRateStats {
  totalSessions: number;      // 总会话数
  totalCrashes: number;       // 总崩溃数
  crashRate: number;          // 崩溃率 = 崩溃会话数 / 总会话数
  affectedUserRate: number;   // 受影响用户率
  topCrashGroups: CrashGroup[]; // Top崩溃
}

export class CrashAnalyzer {
  private static instance: CrashAnalyzer;
  private pref: preferences.Preferences | null = null;
  private crashGroups: Map<string, CrashGroup> = new Map();
  private totalSessions: number = 0;
  private crashSessions: number = 0;
  
  static getInstance(): CrashAnalyzer {
    if (!CrashAnalyzer.instance) {
      CrashAnalyzer.instance = new CrashAnalyzer();
    }
    return CrashAnalyzer.instance;
  }
  
  async init(context: common.UIAbilityContext): Promise<void> {
    this.pref = await preferences.getPreferences(context, 'crash_analysis');
  }
  
  // 处理新崩溃
  processCrash(crashInfo: CrashInfo): void {
    // 生成崩溃签名——用于聚合同类崩溃
    const signature = this.generateSignature(crashInfo);
    
    if (this.crashGroups.has(signature)) {
      // 已有同类崩溃,更新统计
      const group = this.crashGroups.get(signature)!;
      group.count++;
      group.lastTime = crashInfo.timestamp;
      if (crashInfo.userId) {
        group.affectedUsers.add(crashInfo.userId);
      }
      // 保留样本(最多10条)
      if (group.samples.length < 10) {
        group.samples.push(crashInfo);
      }
      // 更新严重程度
      group.severity = this.calculateSeverity(group);
    } else {
      // 新崩溃,创建分组
      const group: CrashGroup = {
        signature: signature,
        crashType: crashInfo.crashType,
        topFrame: this.extractTopFrame(crashInfo.crashStack),
        count: 1,
        firstTime: crashInfo.timestamp,
        lastTime: crashInfo.timestamp,
        affectedUsers: new Set(crashInfo.userId ? [crashInfo.userId] : []),
        severity: CrashSeverity.MEDIUM,
        samples: [crashInfo],
      };
      this.crashGroups.set(signature, group);
    }
    
    this.crashSessions++;
  }
  
  // 记录会话数
  recordSession(): void {
    this.totalSessions++;
  }
  
  // 获取崩溃率统计
  getCrashRateStats(): CrashRateStats {
    const crashRate = this.totalSessions > 0 
      ? this.crashSessions / this.totalSessions 
      : 0;
    
    const allAffectedUsers = new Set<string>();
    this.crashGroups.forEach(group => {
      group.affectedUsers.forEach(user => allAffectedUsers.add(user));
    });
    
    // 按崩溃次数排序,取Top10
    const topCrashGroups = Array.from(this.crashGroups.values())
      .sort((a, b) => b.count - a.count)
      .slice(0, 10);
    
    return {
      totalSessions: this.totalSessions,
      totalCrashes: this.crashSessions,
      crashRate: Math.round(crashRate * 10000) / 100,
      affectedUserRate: 0, // 需要总用户数才能计算
      topCrashGroups: topCrashGroups,
    };
  }
  
  // 生成崩溃签名
  private generateSignature(crashInfo: CrashInfo): string {
    // 基于崩溃类型+顶层调用帧生成签名
    const topFrame = this.extractTopFrame(crashInfo.crashStack);
    return `${crashInfo.crashType}::${topFrame}`;
  }
  
  // 提取顶层调用帧
  private extractTopFrame(stack: string): string {
    try {
      // 尝试从堆栈中提取第一个应用代码帧
      const lines = stack.split('\n');
      for (const line of lines) {
        if (line.includes('.ets:') || line.includes('.ts:')) {
          return line.trim();
        }
      }
      return lines[0] || 'unknown';
    } catch (e) {
      return 'unknown';
    }
  }
  
  // 计算严重程度
  private calculateSeverity(group: CrashGroup): CrashSeverity {
    const rate = this.totalSessions > 0 
      ? group.count / this.totalSessions 
      : 0;
    
    if (rate > 0.01) return CrashSeverity.CRITICAL;  // >1%
    if (rate > 0.001) return CrashSeverity.HIGH;      // 0.1%-1%
    if (rate > 0.0001) return CrashSeverity.MEDIUM;   // 0.01%-0.1%
    return CrashSeverity.LOW;
  }
  
  // 打印崩溃分析报告
  printReport(): void {
    const stats = this.getCrashRateStats();
    
    console.info('\n========== 崩溃分析报告 ==========');
    console.info(`总会话数: ${stats.totalSessions}`);
    console.info(`崩溃会话数: ${stats.totalCrashes}`);
    console.info(`崩溃率: ${stats.crashRate}%`);
    console.info(`崩溃分组数: ${this.crashGroups.size}`);
    
    console.info('\nTop崩溃:');
    stats.topCrashGroups.forEach((group, index) => {
      console.info(
        `\n[${index + 1}] ${group.signature}`
      );
      console.info(`  类型: ${group.crashType}`);
      console.info(`  次数: ${group.count}`);
      console.info(`  影响用户: ${group.affectedUsers.size}`);
      console.info(`  严重程度: ${group.severity}`);
      console.info(`  顶层帧: ${group.topFrame}`);
    });
    
    console.info('\n====================================\n');
  }
}

完整示例:崩溃率监控与告警

崩溃率超过阈值自动告警,不用人盯着看。

// CrashMonitor.ets - 崩溃率监控与告警
import { hiAppEvent } from '@kit.PerformanceAnalysisKit';
import { analytics } from '@kit.AnalyticsKit';
import { CrashCollector, CrashInfo } from './CrashCollector';
import { CrashAnalyzer, CrashSeverity, CrashRateStats } from './CrashAnalyzer';
import { common } from '@kit.AbilityKit';

// 告警配置
export interface AlertConfig {
  crashRateThreshold: number;     // 崩溃率阈值(百分比)
  crashCountThreshold: number;    // 单个崩溃次数阈值
  checkIntervalMs: number;        // 检查间隔(毫秒)
  alertChannels: AlertChannel[];  // 告警渠道
}

export enum AlertChannel {
  LOG = 'log',               // 控制台日志
  EVENT = 'event',           // 上报事件
  PUSH = 'push',             // 推送通知
  WEBHOOK = 'webhook',       // Webhook回调
}

// 告警信息
export interface CrashAlert {
  alertType: string;
  message: string;
  severity: CrashSeverity;
  crashRate: number;
  topCrash: string;
  timestamp: number;
}

export class CrashMonitor {
  private static instance: CrashMonitor;
  private collector: CrashCollector;
  private analyzer: CrashAnalyzer;
  private config: AlertConfig;
  private timerId: number = -1;
  private lastAlertTime: number = 0;
  
  // 默认告警配置
  private static readonly DEFAULT_CONFIG: AlertConfig = {
    crashRateThreshold: 0.5,      // 崩溃率>0.5%告警
    crashCountThreshold: 50,      // 单个崩溃>50次告警
    checkIntervalMs: 60000,       // 每分钟检查一次
    alertChannels: [AlertChannel.LOG, AlertChannel.EVENT],
  };
  
  // 告警冷却时间——同一告警5分钟内不重复
  private static readonly ALERT_COOLDOWN_MS = 5 * 60 * 1000;
  
  static getInstance(): CrashMonitor {
    if (!CrashMonitor.instance) {
      CrashMonitor.instance = new CrashMonitor();
    }
    return CrashMonitor.instance;
  }
  
  constructor() {
    this.collector = CrashCollector.getInstance();
    this.analyzer = CrashAnalyzer.getInstance();
    this.config = CrashMonitor.DEFAULT_CONFIG;
  }
  
  // 初始化
  init(context: common.UIAbilityContext, config?: Partial<AlertConfig>): void {
    this.collector.init(context);
    
    if (config) {
      this.config = { ...CrashMonitor.DEFAULT_CONFIG, ...config };
    }
    
    // 启动定时检查
    this.startMonitoring();
    
    console.info('[CrashMonitor] 崩溃监控启动');
  }
  
  // 启动监控
  private startMonitoring(): void {
    // 定时检查崩溃率
    this.timerId = setInterval(() => {
      this.checkCrashRate();
    }, this.config.checkIntervalMs) as unknown as number;
  }
  
  // 停止监控
  stopMonitoring(): void {
    if (this.timerId !== -1) {
      clearInterval(this.timerId);
      this.timerId = -1;
    }
  }
  
  // 记录会话——每次应用启动时调用
  recordSession(): void {
    this.analyzer.recordSession();
  }
  
  // 检查崩溃率
  private checkCrashRate(): void {
    const stats = this.analyzer.getCrashRateStats();
    
    // 检查整体崩溃率
    if (stats.crashRate > this.config.crashRateThreshold) {
      this.sendAlert({
        alertType: 'crash_rate_exceeded',
        message: `崩溃率${stats.crashRate}%超过阈值${this.config.crashRateThreshold}%`,
        severity: CrashSeverity.CRITICAL,
        crashRate: stats.crashRate,
        topCrash: stats.topCrashGroups[0]?.signature || 'unknown',
        timestamp: Date.now(),
      });
    }
    
    // 检查单个崩溃次数
    for (const group of stats.topCrashGroups) {
      if (group.count > this.config.crashCountThreshold) {
        this.sendAlert({
          alertType: 'crash_count_exceeded',
          message: `崩溃${group.signature}发生${group.count}次,超过阈值${this.config.crashCountThreshold}`,
          severity: group.severity,
          crashRate: stats.crashRate,
          topCrash: group.signature,
          timestamp: Date.now(),
        });
      }
    }
  }
  
  // 发送告警
  private sendAlert(alert: CrashAlert): void {
    // 冷却检查
    if (Date.now() - this.lastAlertTime < CrashMonitor.ALERT_COOLDOWN_MS) {
      return;
    }
    this.lastAlertTime = Date.now();
    
    // 按配置的渠道发送告警
    for (const channel of this.config.alertChannels) {
      switch (channel) {
        case AlertChannel.LOG:
          console.error(`[CrashMonitor] ⚠️ 告警: ${alert.message}`);
          break;
          
        case AlertChannel.EVENT:
          // 上报告警事件
          analytics.reportEvent('crash_alert', {
            'alert_type': alert.alertType,
            'message': alert.message,
            'severity': alert.severity,
            'crash_rate': alert.crashRate,
            'top_crash': alert.topCrash,
          });
          break;
          
        case AlertChannel.PUSH:
          // 发送推送通知
          this.sendPushAlert(alert);
          break;
          
        case AlertChannel.WEBHOOK:
          // 调用Webhook
          this.sendWebhookAlert(alert);
          break;
      }
    }
  }
  
  private sendPushAlert(alert: CrashAlert): void {
    // 实现推送告警逻辑
    console.info('[CrashMonitor] 推送告警已发送');
  }
  
  private async sendWebhookAlert(alert: CrashAlert): Promise<void> {
    // 实现Webhook告警逻辑
    // 实际项目中用HTTP请求发送到钉钉/飞书/企业微信
    console.info('[CrashMonitor] Webhook告警已发送');
  }
  
  // 获取崩溃报告
  getCrashReport(): CrashRateStats {
    return this.analyzer.getCrashRateStats();
  }
  
  // 打印崩溃报告
  printReport(): void {
    this.analyzer.printReport();
  }
}

踩坑与注意事项

1. 崩溃采集初始化要最早

崩溃采集必须在所有业务代码之前初始化。如果初始化晚了,之前的崩溃就捕获不到。放在EntryAbility的onCreate第一行。

2. 崩溃堆栈可能被混淆

如果你开启了代码混淆,崩溃堆栈里看到的是a.b.c这种混淆后的名字,根本看不懂。必须保留mapping文件,用符号化工具还原。

// 构建配置中保留混淆映射
// build-profile.json5
{
  "app": {
    "proguard": {
      "enable": true,
      "keepMappingFile": true  // 保留mapping文件!
    }
  }
}

3. 别在崩溃回调里做重活

崩溃回调里不能做耗时操作,更不能发网络请求——因为应用已经处于崩溃状态,随时可能被系统杀掉。正确的做法是:把崩溃信息存到本地,下次启动时再上报。

4. 区分崩溃和ANR

崩溃是应用直接挂了,ANR是应用卡住了但还没挂。两者原因不同,处理方式也不同。崩溃看堆栈,ANR看主线程在干什么。

5. 崩溃率计算口径

崩溃率 = 崩溃会话数 / 总会话数。注意是"会话"不是"用户"——一个用户一天可能打开10次应用,每次都是一个会话。如果崩溃率按用户算,数字会偏低。

HarmonyOS 6适配说明

HarmonyOS 6在崩溃分析方面的更新:

  1. CrashCollection增强:崩溃采集更完整,支持捕获更多类型的崩溃
  2. 实时崩溃看板:DevEco Studio集成崩溃实时看板,开发阶段就能看到崩溃数据
  3. 智能根因分析:AI辅助分析崩溃根因,自动推荐修复方案
  4. 崩溃率SLA:新增崩溃率SLA管理,设定目标崩溃率并自动追踪
// HarmonyOS 6 智能根因分析
async function analyzeCrashRootCause(crashId: string): Promise<void> {
  try {
    const result = await hiAppEvent.analyzeCrash({
      crashId: crashId,
      includeSuggestion: true,  // 包含修复建议
    });
    
    console.info(`根因: ${result.rootCause}`);
    console.info(`建议: ${result.suggestion}`);
    console.info(`置信度: ${result.confidence}`);
  } catch (error) {
    console.error('[Crash] 分析失败');
  }
}

总结

崩溃是线上最严重的问题——用户遇到崩溃,体验直接归零。CrashCollection集成崩溃采集,堆栈分析定位根因,崩溃率监控实时告警,这三步是崩溃分析的基本功。

维度 评价
学习难度 ⭐⭐☆☆☆ SDK接入简单,堆栈分析需要经验
使用频率 ⭐⭐⭐⭐⭐ 持续监控,每次发版重点关注
重要程度 ⭐⭐⭐⭐⭐ 崩溃直接影响用户体验和留存

核心记住三点:初始化要最早(放在onCreate第一行)、混淆要保留mapping(否则堆栈看不懂)、崩溃率按会话算(别按用户算)。崩溃率是应用质量的底线——0.1%以下算合格,1%以上就得紧急修复。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。