为什么UI个重进程之前的进程没有先执行

why UI processes before a heavy process are not executed first

考虑像这样的简单代码:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInastanceState){
        super.onCreate(savedInastanceState);
        setContentView(R.layout.main);
        final ProgressDialog pdUpdate = new ProgressDialog(this);
        final Button btn = (Button)findViewById(R.id.btnUpdate);
         btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){
                btn.setText(Calendar.getInstance().getTime().toString());
                int i=0;
                while (i<100){
                    i++;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.i("test","i = " + i);
                }
            }
        });

    }
}

点击 btn 会发生两件事:更改按钮的文本并计数 i 到 100,延迟 100 毫秒(这只是模拟繁重过程的示例,例如读取文件、下载等)。
我知道实现此类代码的正确方法是使用 AsyncTask 但我的问题是关于如何编译此代码的。它是一个单线程应用程序。所以编译器先读取btn.setText(Calendar.getInstance().getTime().toString());,只有在执行完这一行后才转到下一行代码(如果我错了请指正),但它并没有发生。为什么?
C# 中有一个 Refresh() 方法可以解决这个问题(只需在 UI 更改和应用更改后调用它)。 java?
中是否有任何类似的方法?感谢您的帮助。

编辑 1
问题是关于以下进程的顺序:
进程 1:

 btn.setText(Calendar.getInstance().getTime().toString());

进程 2:

                    i++;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.i("test","i = " + i);
                }

无论哪个先到(我的意思是 btn.onClick(){process1;process2;}btn.onClick(){process2;process1;}),第二个进程总是先执行。我的意思是首先我在 Logcat 中看到 i 的计数,然后看到按钮文本的变化。

Android 正在管理自己的绘图周期。但是您正在与它共享主线程。您可以在计划重绘视图的视图上调用 invalidate()。但是你必须在 Android 的主线程上腾出一些时间来刷新 UI 无论如何(setText() 可能正在调用 invalidate())。

nonono,只有你的方法 运行 finish 才能工作(exp: just sleep 100 msc without while loop)。你可以在 google 中找到 java 方法的工作原理。 (我的英文很差)

正如许多人在评论中指出的那样,由于您在 onClickListener 中执行 while 循环,因此 UI 线程永远没有机会 invalidate 您的按钮,从而重绘所述按钮。

我想如果你想解决这个问题,你可以简单地让另一个监听器与重绘过程完成相关。看下面这篇answer:

public class DrawListenerView extends View{
    private Callback callback;
    public DrawListenerView(Callback callback){
        this.callback = callback;
    }

    @Override
    protected void onDraw (Canvas canvas){
        super.onDraw(canvas);

        //add your method here you want to call
        //Or use a Callback-pattern 
        callback.finish();
    } 
}

public interface Callback(){
    public void finish();
}

按钮绘制完成后,您就可以开始循环睡眠了。这样就给你画好了新的文字,然后就睡了。

我不确定这个应用程序是什么,但是如果您不希望用户在任何时间段内再次单击它,您可以考虑在他们按下按钮后将其禁用一段时间.

在这种情况下,您不需要调用任何等效的 refresh()。但是 Button.setText() 不会自动重绘视图层次结构。相反,它向上传递视图层次结构的信息,即文本已更改并且需要重绘。最终,此信息到达视图层次结构的根,进而通知 ChoreographerChoreographer 安排下一帧的绘图。这反过来会存储在您的 UI 线程的消息队列中。

因此,当您将 UI 线程置于休眠状态时,不会重绘布局,而是计划重绘。一旦您的线程空闲,消息队列中的消息就会开始执行。在某些时候,Choreographer 被重绘消息调用并命令视图层次结构重绘自身。

还要考虑像 Handler.post(Runnable)View.post(Runnable) 和它们的 postDelayed 对应方法,如果不需要进行大量计算,可以作为 AsyncTask 的替代方法,而是为以后安排一些操作(例如视图更新)。它们使用与上述相同的机制 - 将 Runnable 放入线程的消息队列中,然后在线程空闲时获取并执行。