You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中CircularProgressIndicator耗时代码结束后才显示的问题求助

问题:耗时操作阻塞主线程导致UI无法实时更新

我尝试在一段耗时代码执行时显示CircularProgressIndicator,但代码运行期间屏幕完全无法刷新,直到代码执行完毕后指示器才开始动画。同时按钮文字也没法实时更改,推测是主线程阻塞导致的。以下是复现问题的最简示例代码:


布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@android:color/black">
        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:textSize="18sp"
            android:text="Start"
            tools:ignore="HardcodedText" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal">
            <com.google.android.material.progressindicator.CircularProgressIndicator
                android:id="@+id/cpi"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:progress="0"
                android:useLevel="true"
                android:visibility="visible"
                app:indicatorColor="#FF0000"
                app:indicatorSize="95dp"
                app:trackColor="#606060"
                app:trackCornerRadius="5dp"
                app:trackThickness="16dp"
                tools:layout_editor_absoluteX="157dp"
                tools:layout_editor_absoluteY="108dp" />
        </LinearLayout>
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

逻辑代码:MainActivity.java

package com.hennep.testcircularprogress;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.os.Handler;
import com.google.android.material.progressindicator.CircularProgressIndicator;

public class MainActivity extends AppCompatActivity {
    int iProgress = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Button button = findViewById(R.id.button);
        final CircularProgressIndicator progress = findViewById(R.id.cpi);

        Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true ) {
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            progress.setProgress(iProgress,true);
                        }
                    },100 );
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e ) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v ) {
                iProgress = 0;
                button.setText("Calculating...");
                for( int k = 0; k < 192; k++ ) {
                    Thread.yield();
                    for( int fk = 1; fk <= 1000; fk *= 10 ) {
                        for( int j = 0; j < 192; j++ ) {
                            for( int fj = 1; fj <= 1000; fj *= 10 ) {
                                for( int i = 0; i < 192; i++ ) {
                                    for( int fi = 1; fi <= 1000; fi *= 10 ) {
                                        // Calculate E192 three resistor resistive divider here, not relevant for the test
                                        iProgress = k;
                                    }
                                }
                            }
                        }
                    }
                }
                button.setText("Start");
            }
        });        
    }
}

原因分析与修复方案

核心原因

你的推测完全正确:onClick方法里的嵌套循环是在主线程执行的。Android的UI更新(包括按钮文字修改、进度条动画)都依赖主线程的消息循环,当主线程被耗时操作阻塞时,所有UI更新任务都会排队等待,直到耗时操作结束才会执行,因此你看不到实时的UI变化。

修复方案:将耗时操作移到子线程

把计算逻辑放到子线程执行,同时在需要更新UI时切换回主线程。以下是两种常用实现方式:

方式1:使用Java AsyncTask(兼容旧项目)

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new AsyncTask<Void, Integer, Void>() {
            // 执行前准备:主线程中更新按钮文字
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                button.setText("Calculating...");
            }

            // 耗时操作:子线程中执行计算
            @Override
            protected Void doInBackground(Void... voids) {
                for (int k = 0; k < 192; k++) {
                    for (int fk = 1; fk <= 1000; fk *= 10) {
                        for (int j = 0; j < 192; j++) {
                            for (int fj = 1; fj <= 1000; fj *= 10) {
                                for (int i = 0; i < 192; i++) {
                                    for (int fi = 1; fi <= 1000; fi *= 10) {
                                        // 你的计算逻辑
                                    }
                                }
                            }
                        }
                    }
                    publishProgress(k); // 发布进度,触发UI更新
                }
                return null;
            }

            // 进度更新:主线程中更新进度条
            @Override
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                progress.setProgress(values[0], true);
            }

            // 执行完成:主线程中恢复按钮文字
            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                button.setText("Start");
            }
        }.execute();
    }
});

方式2:使用Kotlin协程(现代Android开发首选)

如果项目支持Kotlin,协程的写法更简洁且更易维护:

// 先在build.gradle中添加协程依赖
// implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"

button.setOnClickListener {
    lifecycleScope.launch { // 绑定Activity生命周期,避免内存泄漏
        button.text = "Calculating..."
        // 切换到IO线程执行耗时计算
        withContext(Dispatchers.IO) {
            for (k in 0 until 192) {
                for (fk in listOf(1, 10, 100, 1000)) {
                    for (j in 0 until 192) {
                        for (fj in listOf(1, 10, 100, 1000)) {
                            for (i in 0 until 192) {
                                for (fi in listOf(1, 10, 100, 1000)) {
                                    // 你的计算逻辑
                                }
                            }
                        }
                    }
                }
                // 切换回主线程更新进度
                withContext(Dispatchers.Main) {
                    progress.setProgress(k, true)
                }
            }
        }
        button.text = "Start"
    }
}

额外优化

  • 移除原代码中那个无限循环的更新线程,直接在计算循环中实时发布进度即可,避免不必要的线程开销。
  • 注意内存泄漏:协程使用lifecycleScope自动绑定生命周期;AsyncTask需要在Activity销毁时调用cancel(true)取消任务。

内容的提问来源于stack exchange,提问作者hennep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 03:59:55