线程如何与 Android 中的 Handler 一起工作?

Posted

技术标签:

【中文标题】线程如何与 Android 中的 Handler 一起工作?【英文标题】:How does a thread work with Handler in Android? 【发布时间】:2021-11-14 08:52:43 【问题描述】:

在下面给出的代码部分中,我在单击按钮时运行了一个线程。一开始,线程会将文本设置到textView,然后它会休眠6秒。但是,实际上在单击按钮时,首先,线程休眠 6 秒,然后将文本设置为 textView。现在,为什么会出现这种语句执行流程不匹配的情况?

public class MainActivity extends AppCompatActivity 

    EditText editText;
    Button button;
    TextView textView;

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

        editText = findViewById(R.id.editText);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                button.setEnabled(false);
                runthread();
            
        );

    

    private void runthread() 
        final String s1 = editText.getText().toString();
        Handler handler = new Handler();
        handler.post(new Runnable() 
            @Override
            public void run() 
                runOnUiThread(new Runnable() 
                    @Override
                    public void run() 
                        textView.setText(s1);
                        button.setEnabled(true);
                        try 
                            Thread.sleep(6000);
                         catch (InterruptedException e) 
                            e.printStackTrace();
                        
                    
                );
            
        );
    

【问题讨论】:

【参考方案1】:

android 主线程建立在Looper 和Handler 两个核心概念之上。它转换为与线程和消息交互器相关的消息循环。所有 ui 员工都从中受益。例如,您使用的setText 只不过是向looper 发送一条新消息,一段时间后该消息被某个处理程序解析。正如您可能首先猜到的那样,每条消息都已入队。

如果你通过上面的棱镜看你的例子,你会看到下面列出的步骤。(所有步骤都在主线程/主循环器上进行)

Handler handler = new Handler(); <-- this handler is related to main looper
        handler.post(new Runnable()    <-- enqueue new message to main looper
            @Override
            public void run() 
                runOnUiThread(new Runnable()  <-- enqueue new message
                    @Override
                    public void run() 
                        textView.setText(s1); <-- enqueue new message of setting text
                        button.setEnabled(true); <-- enqueue new message of enabling button
                        try 
                            Thread.sleep(6000); <-- suspend thread for 6 seconds
                         catch (InterruptedException e) 
                            e.printStackTrace();
                        
                    
                );
            
        );

根据上述,在处理所有消息(包括设置文本和启用按钮)之前暂停线程。这样做的结果是顺序颠倒。

#UPDATE 那么,我怎样才能让这个 sleep() 方法也入队呢?

     val handler = Handler();
        handler.post 
            runOnUiThread 
                textView.text = "ABC";
                button.isEnabled = true;

            
        
        handler.postDelayed(
            try 
                Thread.sleep(6000);
             catch (e: InterruptedException) 
                e.printStackTrace();
            
        , 16) <-- 16 milliseconds is a time between frames

【讨论】:

你是想说 Thread.sleep() 方法没有入队直接执行?那么,我怎样才能将这个 sleep() 方法也加入队列呢? 是的,这正是我所说的。活套与线相连。如果线程休眠,则不处理消息。您的第二个问题的答案在更新的答案中。顺便说一句,runOnUiThread 只不过是底层处理程序上的另一个 post 方法。 如果我想在设置文本和启用按钮之间加入 Thread.sleep() 方法怎么办? 这个例子很好地解释了处理程序和循环器的工作,但你不应该在主线程上使用Thread.sleep。这不是一个好的做法,因为这样会阻止用户与应用程序的交互。我建议研究另一种方法来满足您的要求。

以上是关于线程如何与 Android 中的 Handler 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

Android面试 Handler机制

Android中的Handler详解以及和Thread的区别

Android中的Handler

Android--多线程之Handler

Android--多线程之Handler

Android 开发: Android 消息处理机制之一: Handler 与 Message