为什么在繁重的进程之前的UI进程不会先执行

时间:2016-11-24 09:25:12

标签: android single-threaded

考虑这样一个简单的代码:

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的计数,然后看到按钮文本的更改。

4 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:0)

nonono,只有你的方法运行完成它才能工作(exp:只需在没有while循环的情况下睡眠100 msc)。你可以在google中找到java方法的工作方式。(我的英语很差)

答案 2 :(得分:0)

正如许多人在评论中指出的那样,由于你在while内进行了onClickListener循环,因此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();
}

一旦按钮的绘制完成,你就可以进行循环睡眠。这将为您提供新的文字,然后再睡觉。

我不确定这个应用程序会是什么,但是如果您不希望用户在任何时间内再次点击它,您可能会考虑在按下该按钮一段时间后禁用该按钮。 / p>

答案 3 :(得分:-1)

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