安卓开发线程池使用详解

举报
yd_217846120 发表于 2026/08/26 08:44:03 2026/08/26
【摘要】 安卓开发线程池使用详解 一、为什么需要线程池在 Android 开发中,主线程(UI 线程)负责所有界面的绘制与交互。如果在主线程中执行耗时操作(网络请求、数据库读写、文件 I/O、复杂计算等),轻则造成界面卡顿,重则触发 ANR(Application Not Responding),系统会弹出"应用无响应"对话框,严重影响用户体验。为了避免阻塞主线程,我们需要把耗时任务放到子线程中执行...

安卓开发线程池使用详解

一、为什么需要线程池

在 Android 开发中,主线程(UI 线程)负责所有界面的绘制与交互。如果在主线程中执行耗时操作(网络请求、数据库读写、文件 I/O、复杂计算等),轻则造成界面卡顿,重则触发 ANR(Application Not Responding),系统会弹出"应用无响应"对话框,严重影响用户体验。

为了避免阻塞主线程,我们需要把耗时任务放到子线程中执行。然而,如果每次执行任务都手动 new Thread(),会带来一系列问题:

问题 说明
开销大 每次新建/销毁线程都有 CPU 与内存开销,频繁创建影响性能
资源失控 无限制地创建线程可能导致 OOM,线程过多还会引发频繁上下文切换
管理困难 缺乏统一的生命周期管理,无法优雅地取消任务、监控状态
复用性差 线程执行完即销毁,无法复用,无法统一控制优先级、命名等

线程池(Thread Pool) 正是为了解决这些问题而生。它预先创建一组可复用的线程,将任务提交给池子执行,从而实现:

  • 降低资源消耗:复用已创建的线程,减少新建/销毁开销
  • 提高响应速度:任务到达时无需等待线程创建即可执行
  • 便于统一管理:可控制最大并发数、优雅关闭、监控线程状态
  • 提供更多能力:支持任务排队、定时任务、拒绝策略等

二、Java 线程池核心体系

Android 的线程池机制直接来源于 Java 的 java.util.concurrent 包(简称 JUC)。理解其核心接口与类是使用线程池的基础。

2.1 核心接口与类的关系

Executor (顶层接口)
  └── ExecutorService (扩展接口,提供生命周期管理与 Future 支持)
        └── AbstractExecutorService (抽象类)
              └── ThreadPoolExecutor (线程池核心实现类)
                    └── ScheduledThreadPoolExecutor (支持定时/周期任务)
Executors (工厂工具类,提供快速创建常用线程池的静态方法)

2.2 Executor 接口

最顶层接口,只定义了一个方法:

public interface Executor {
    void execute(Runnable command);
}

它的设计哲学是将任务提交与任务执行解耦。调用者只需关心"提交什么任务",不必关心"任务如何执行、在哪个线程执行"。

2.3 ExecutorService 接口

ExecutorServiceExecutor 基础上扩展了生命周期管理与结果返回能力:

// 提交有返回值的任务
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);

// 批量执行任务,返回所有结果
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

// 批量执行任务,返回最先完成的结果
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

// 生命周期管理
void shutdown();           // 温和关闭:不再接受新任务,已提交任务继续执行完
List<Runnable> shutdownNow(); // 立即关闭:尝试中断所有任务,返回未执行的任务列表
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

2.4 Future 与 Callable

  • Runnable:无返回值、无法抛出受检异常的任务。
  • Callable<V>:有返回值、可以抛出异常的任务。
  • Future<V>:代表一个异步计算的结果,提供取消、查询状态、获取结果的能力。
Future<String> future = executor.submit(() -> {
    Thread.sleep(2000);
    return "结果数据";
});

boolean done = future.isDone();
String result = future.get();              // 阻塞等待结果
String r2 = future.get(1, TimeUnit.SECONDS); // 超时等待
boolean canceled = future.cancel(true);    // 尝试取消任务

三、ThreadPoolExecutor 详解

ThreadPoolExecutor 是线程池的真正核心。掌握它的构造参数,就掌握了线程池的全部行为。

3.1 完整构造方法

public ThreadPoolExecutor(
    int corePoolSize,                       // 核心线程数
    int maximumPoolSize,                   // 最大线程数
    long keepAliveTime,                    // 非核心线程的空闲存活时间
    TimeUnit unit,                         // 存活时间单位
    BlockingQueue<Runnable> workQueue,     // 任务队列
    ThreadFactory threadFactory,           // 线程工厂
    RejectedExecutionHandler handler       // 拒绝策略
)

3.2 七大参数详解

参数 含义 建议取值
corePoolSize 核心线程数,即使空闲也会保留(除非设置 allowCoreThreadTimeOut(true) 根据 CPU 核数与任务类型设定
maximumPoolSize 线程池能创建的最大线程数 CPU 密集型 ≈ N+1;IO 密集型 ≈ 2N 或更多
keepAliveTime 非核心线程空闲后的存活时间,超时回收 30~60 秒常见
unit 时间单位 TimeUnit.SECONDS
workQueue 存放等待执行任务的阻塞队列 根据场景选择有界/无界队列
threadFactory 创建线程的工厂,可自定义线程名、优先级、是否守护线程 建议自定义以便定位问题
handler 当任务无法被接受(队列满且线程数达上限)时的处理策略 根据业务选择

3.3 任务执行流程(重要!)

理解线程池如何处理一个新提交的任务,是正确配置线程池的关键:

提交一个新任务
      │
      ▼
当前线程数 < corePoolSize ?
      │ 是
      ▼ → 创建核心线程执行任务
      │ 否
      ▼
任务队列未满 ?
      │ 是
      ▼ → 任务入队等待
      │ 否
      ▼
当前线程数 < maximumPoolSize ?
      │ 是
      ▼ → 创建非核心线程执行任务
      │ 否
      ▼
执行拒绝策略

关键点:线程池优先填满核心线程,再填满队列,最后才创建非核心线程。这意味着如果使用无界队列(如 LinkedBlockingQueue 无参构造),maximumPoolSize 将永远无效,因为队列永远不会满。

3.4 常用阻塞队列

队列 特点 适用场景
ArrayBlockingQueue 有界,基于数组,FIFO 需要严格控制堆积任务数
LinkedBlockingQueue 默认无界(Integer.MAX_VALUE),基于链表,FIFO 通用场景,注意无界风险
SynchronousQueue 不存储元素,每个 put 必须等待一个 take 直接传递,配合大 maxPoolSize
PriorityBlockingQueue 支持优先级排序的无界队列 任务有优先级
DelayQueue 延迟队列,元素到期才能取出 定时任务

3.5 拒绝策略

JDK 内置四种拒绝策略,均实现 RejectedExecutionHandler 接口:

策略 行为 适用场景
AbortPolicy(默认) 抛出 RejectedExecutionException 默认,需捕获处理
CallerRunsPolicy 由提交任务的线程自己执行该任务 不丢任务,降低提交速度(背压)
DiscardPolicy 静默丢弃新任务 允许丢失、不关心
DiscardOldestPolicy 丢弃队列中最旧的任务,再尝试提交 保留最新任务

自定义拒绝策略示例

public class LogAndDiscardPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        Log.w("ThreadPool", "任务被拒绝,当前队列大小=" + executor.getQueue().size());
        // 可写入埋点、持久化任务稍后重试等
    }
}

四、Executors 工厂方法及适用场景

Executors 提供了快速创建常用线程池的静态方法。但 Android/Java 官方都不推荐在生产环境直接使用,原因后文详述。这里先了解它们的行为。

4.1 newFixedThreadPool(固定大小线程池)

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • 核心线程数 = 最大线程数 = nThreads
  • 使用无界 LinkedBlockingQueue
  • 线程数固定,不会回收
  • 风险:队列无界,任务堆积可能导致 OOM

适用:任务量已知且较平稳的场景。

4.2 newCachedThreadPool(可缓存线程池)

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 核心线程数为 0,最大线程数为 Integer.MAX_VALUE
  • 使用 SynchronousQueue,不缓存任务
  • 空闲线程 60 秒后回收
  • 风险:可能创建大量线程导致 OOM

适用:大量短耗时、轻量异步任务。

4.3 newSingleThreadExecutor(单线程线程池)

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 只有一个工作线程,任务按提交顺序串行执行
  • 风险:无界队列同样可能 OOM

适用:需要保证任务串行执行的场景(如日志写入、数据库串行操作)。

4.4 newScheduledThreadPool(定时任务线程池)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
  • 返回 ScheduledExecutorService,支持定时与周期性任务
  • 内部使用 DelayedWorkQueue
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// 延迟 3 秒执行一次
scheduler.schedule(() -> Log.d("TAG", "一次性任务"), 3, TimeUnit.SECONDS);

// 固定频率:每 5 秒执行一次(不管上次是否完成)
scheduler.scheduleAtFixedRate(() -> doWork(), 0, 5, TimeUnit.SECONDS);

// 固定延迟:上次执行结束后再等 5 秒执行下一次
scheduler.scheduleWithFixedDelay(() -> doWork(), 0, 5, TimeUnit.SECONDS);

两者区别

  • scheduleAtFixedRate:以任务开始时间为基准计算下次执行,任务执行慢时可能"追赶"或重叠。
  • scheduleWithFixedDelay:以任务结束时间为基准,永远保证两次执行间有指定间隔。

4.5 为什么不推荐直接用 Executors

阿里巴巴 Java 开发规约明确禁止使用 Executors 创建线程池,原因:

  1. newFixedThreadPoolnewSingleThreadExecutor 使用无界队列,任务堆积 → OOM
  2. newCachedThreadPoolnewScheduledThreadPool 最大线程数为 Integer.MAX_VALUE,线程数失控 → OOM
  3. 无法自定义线程名,出问题时线程堆栈难以定位
  4. 无法根据业务定制拒绝策略

正确做法:使用 ThreadPoolExecutor 显式构造,并使用有界队列与合理的上限。


五、Android 中线程池的正确用法

5.1 自定义线程池(推荐模板)

public class AppExecutors {

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    private static class AppThreadFactory implements ThreadFactory {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        AppThreadFactory(String prefix) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            namePrefix = prefix + "-" + POOL_NUMBER.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon()) t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    // IO 密集型线程池:用于网络、磁盘读写
    public final ExecutorService ioPool = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(128),
            new AppThreadFactory("io"),
            new ThreadPoolExecutor.CallerRunsPolicy());

    // CPU 密集型线程池:用于解码、计算
    public final ExecutorService cpuPool = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            CORE_POOL_SIZE,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            new AppThreadFactory("cpu"));

    // 单线程池:用于日志、数据库串行
    public final ExecutorService singlePool = new ThreadPoolExecutor(
            1, 1, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            new AppThreadFactory("single"));

    private static volatile AppExecutors instance;

    public static AppExecutors get() {
        if (instance == null) {
            synchronized (AppExecutors.class) {
                if (instance == null) instance = new AppExecutors();
            }
        }
        return instance;
    }
}

使用:

AppExecutors.get().ioPool.execute(() -> {
    String data = doNetworkRequest();
    runOnUiThread(() -> textView.setText(data));
});

5.2 线程数配置经验

CPU 核数获取:

int cpuCount = Runtime.getRuntime().availableProcessors();
任务类型 推荐核心线程数 说明
CPU 密集型(解码、加解密、JSON 解析、复杂计算) N + 1 多出的 1 用于在偶发阻塞(如缺页中断)时仍保持 CPU 利用率
IO 密集型(网络、磁盘、数据库) 2N 或更多 线程大部分时间在等待 IO,可适当多开

注意:以上是经验值,实际需结合压测与监控调整。Android 设备 CPU 核数差异大(4~8 核常见),不要硬编码。

5.3 切回主线程更新 UI

子线程不能直接更新 UI。常见切回主线程方式:

// 方式一:Activity.runOnUiThread
runOnUiThread(() -> textView.setText(result));

// 方式二:View.post
textView.post(() -> textView.setText(result));

// 方式三:Handler
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> textView.setText(result));

5.4 与 Lifecycle 绑定,避免内存泄漏与崩溃

在 Activity/Fragment 销毁时,正在执行的任务若仍持有 View/Context 引用,可能导致内存泄漏;若任务完成后更新已销毁的 UI,会崩溃。

做法:在 onDestroy 中关闭线程池或取消任务。

public class MyActivity extends AppCompatActivity {
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private Future<?> pendingTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        pendingTask = executor.submit(() -> {
            String data = loadData();
            runOnUiThread(() -> textView.setText(data));
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (pendingTask != null) pendingTask.cancel(true);
        executor.shutdownNow();
    }
}

更优雅的方案是结合 Lifecycle

public class LifecycleAwareExecutor implements LifecycleObserver {
    private final ExecutorService pool;
    private final List<Future<?>> tasks = new ArrayList<>();

    public LifecycleAwareExecutor(ExecutorService pool, LifecycleOwner owner) {
        this.pool = pool;
        owner.getLifecycle().addObserver(this);
    }

    public void execute(Runnable r) {
        tasks.add(pool.submit(r));
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy() {
        for (Future<?> f : tasks) f.cancel(true);
        pool.shutdownNow();
    }
}

六、Android 专属异步机制对比

Android 历史上提供了多种异步机制,它们与线程池的关系如下:

机制 现状 与线程池关系
AsyncTask API 30 已废弃 内部使用串行线程池(SERIAL_EXECUTOR),可改为并行
HandlerThread 仍可用 自带 Looper 的子线程,本质是单线程消息循环
IntentService API 30 已废弃 内部用 HandlerThread 串行处理
Loader 已废弃 异步加载数据,生命周期友好
ThreadPoolExecutor 推荐 最灵活、可控
RxJava / Coroutines 推荐 提供调度器封装,底层仍可用线程池

6.1 HandlerThread 示例

HandlerThread worker = new HandlerThread("worker-thread");
worker.start();
Handler workerHandler = new Handler(worker.getLooper());

workerHandler.post(() -> {
    // 在 worker 线程执行
    String data = loadData();
    runOnUiThread(() -> textView.setText(data));
});

// 销毁时
worker.quitSafely();

6.2 Kotlin 协程与线程池

协程是当前 Android 推荐的异步方案,其调度器底层可复用线程池:

// 自定义调度器,复用前面定义的 ioPool
val ioDispatcher = AppExecutors.get().ioPool.asCoroutineDispatcher()

scope.launch(ioDispatcher) {
    val data = loadData()        // 在 ioPool 线程执行
    withContext(Dispatchers.Main) {
        textView.text = data     // 切回主线程
    }
}

七、常见陷阱与最佳实践

7.1 陷阱清单

陷阱 1:使用无界队列导致 OOM

// 错误:LinkedBlockingQueue 无参构造容量为 Integer.MAX_VALUE
new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

// 正确:使用有界队列
new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200));

陷阱 2:maximumPoolSize 形同虚设

当队列无界时,任务永远不会走到"创建非核心线程"分支,maximumPoolSize 永远无效。要让 maximumPoolSize 生效,必须使用有界队列。

陷阱 3:线程池未关闭导致内存泄漏

线程池的工作线程是非守护线程,JVM/进程不会因主线程结束而退出。在 Activity/Fragment/Service 销毁时务必调用 shutdownshutdownNow

陷阱 4:在拒绝策略中再次提交任务造成死循环

自定义拒绝策略时,若在其中再次调用 executor.execute 而池子仍满,会再次触发拒绝,形成无限递归。

陷阱 5:误用 executesubmit

  • execute(Runnable):无返回值,异常会触发 UncaughtExceptionHandler,默认打印到 stderr。
  • submit(Callable/Runnable):返回 Future异常被封装在 Future 内,必须调用 future.get() 才会抛出;若不调用,异常会被"吞掉"。
Future<?> f = executor.submit(() -> { throw new RuntimeException("boom"); });
// 异常不会立即出现,必须:
try { f.get(); } catch (ExecutionException e) { Log.e("TAG", "任务异常", e.getCause()); }

陷阱 6:scheduleAtFixedRate 中任务抛异常导致周期停止

ScheduledThreadPoolExecutor 的周期任务若抛出未捕获异常,后续不再执行。务必在任务内部 try-catch。

scheduler.scheduleAtFixedRate(() -> {
    try { doWork(); }
    catch (Throwable t) { Log.e("TAG", "周期任务异常", t); }
}, 0, 5, TimeUnit.SECONDS);

陷阱 7:共享线程池被慢任务拖垮

一个慢任务占满核心线程且队列满后,会阻塞后续所有任务。建议按业务隔离线程池(IO、CPU、DB、日志各自独立)。

7.2 最佳实践总结

  1. 禁止使用 Executors 工厂方法,统一用 ThreadPoolExecutor 显式构造。
  2. 使用有界队列,避免任务无限堆积。
  3. 自定义 ThreadFactory,给线程命名,便于排查问题。
  4. 按业务隔离线程池,避免相互影响。
  5. 与生命周期绑定,在销毁时关闭或取消任务。
  6. 任务内部 try-catch,避免异常吞没或周期任务停止。
  7. 合理设置拒绝策略,关键任务用 CallerRunsPolicy 或自定义持久化重试。
  8. 监控线程池状态,必要时上报 getActiveCountgetQueue().size()getCompletedTaskCount()
  9. CPU 密集型与 IO 密集型分开配置,不要用一个池子混跑。
  10. 优先考虑 Kotlin 协程或 RxJava,它们在底层复用线程池并提供更安全的生命周期管理。

八、监控与调优

8.1 关键监控指标

ThreadPoolExecutor pool = ...;
int active = pool.getActiveCount();              // 活跃线程数
long completed = pool.getCompletedTaskCount();   // 已完成任务数
long taskCount = pool.getTaskCount();            // 计划任务总数
int queueSize = pool.getQueue().size();          // 当前队列堆积
int largest = pool.getLargestPoolSize();         // 历史最大并发数
int current = pool.getPoolSize();                // 当前线程数

可周期性采样并上报到监控平台,观察:

  • 队列堆积是否持续增长(说明处理能力不足)
  • 拒绝次数是否上升(说明容量不足)
  • 活跃线程是否长期等于最大值(说明可能需要扩容)

8.2 动态调整参数

ThreadPoolExecutor 支持运行时调整部分参数:

pool.setCorePoolSize(newCore);
pool.setMaximumPoolSize(newMax);
pool.setKeepAliveTime(60, TimeUnit.SECONDS);

可结合监控实现自适应调优(如根据负载动态扩缩容)。

8.3 优雅停机

pool.shutdown();                              // 不再接受新任务,等待已提交任务完成
if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
    List<Runnable> unfinished = pool.shutdownNow(); // 超时则强制中断
    Log.w("TAG", "仍有 " + unfinished.size() + " 个任务未完成");
}

九、完整实战示例:带超时与取消的图片加载

public class ImageLoader {
    private final ExecutorService pool;
    private final Map<String, Future<?>> tasks = new ConcurrentHashMap<>();

    public ImageLoader() {
        int cpu = Runtime.getRuntime().availableProcessors();
        pool = new ThreadPoolExecutor(
                Math.max(2, cpu - 1),
                cpu * 2,
                30, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(64),
                r -> {
                    Thread t = new Thread(r, "img-loader");
                    t.setPriority(Thread.NORM_PRIORITY - 1);
                    return t;
                },
                new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    public void load(String url, ImageView target, long timeoutMs) {
        cancel(url);
        Future<?> f = pool.submit(() -> {
            try {
                Bitmap bmp = downloadAndDecode(url, timeoutMs);
                target.post(() -> target.setImageBitmap(bmp));
            } catch (Throwable t) {
                Log.e("ImageLoader", "加载失败: " + url, t);
            }
        });
        tasks.put(url, f);
    }

    public void cancel(String url) {
        Future<?> f = tasks.remove(url);
        if (f != null) f.cancel(true);
    }

    public void shutdown() {
        pool.shutdownNow();
    }
}

十、小结

线程池是 Android 异步编程的基础设施,理解其内部机制远比记住几个工厂方法重要。核心要点回顾:

  1. 线程池通过复用线程降低开销,通过队列与拒绝策略实现流量控制。
  2. ThreadPoolExecutor 的七大参数决定了它的全部行为,务必显式构造、使用有界队列。
  3. 任务执行顺序为:核心线程 → 队列 → 非核心线程 → 拒绝策略。
  4. Android 中务必与 Lifecycle 绑定,避免内存泄漏与 UI 崩溃。
  5. 按业务隔离线程池,CPU 密集型与 IO 密集型分开配置。
  6. 任务内部 try-catch,避免异常被吞或周期任务停止。
  7. 现代开发可优先采用 Kotlin 协程,其底层仍可复用线程池,兼具性能与安全性。

掌握线程池,不仅是写出高效、稳定的 Android 应用的前提,更是理解并发编程的必经之路。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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