为什么要使用Handler?

9

我在一个非常基础的Handler教程中遇到了这段代码。这段代码可以正常工作,但是我不明白为什么我要使用Handler来进行progressDialog.dismiss()操作??? 我将Handler部分删除,并将progressDialog.dismiss()放置在run()方法中,它也可以正常工作。那么为什么要使用Handler呢?

 import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;


    public class HandlerThread extends Activity{

    private Button start;
    private ProgressDialog progressDialog;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        start = (Button) findViewById(R.id.Button01);
        start.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                fetchData();
            }

        });
    }



    protected void fetchData() {
        // TODO Auto-generated method stub
        progressDialog = ProgressDialog.show(this, "", "Doing...");
        new Thread() {
            public void run() {
                try {

                    Thread.sleep(8000);

                    } catch (InterruptedException e) {

                    }
                      messageHandler.sendEmptyMessage(0);

                    }
        }.start();


    }



    private Handler messageHandler = new Handler() {

        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            progressDialog.dismiss();

        }
    };
}

你的观察有误。在非 UI 线程中运行的 run() 方法内调用 progressDialog.dismiss() 会抛出异常。 - Sameer
@Sameer 不一定。请看我的回答 - Dheeraj Vepakomma
在编程中,不能从工作线程(或后台线程)更改任何视图的状态。只能从“UI”线程更改它,因此通常使用“Handler”或“AsyncTask”来实现这种方法。 - Simon Dorociak
@deceiver 更好的是指出AsyncTaskdoInBackground()方法在非UI线程上运行。因此,不能从中调用View的方法。但是,AsyncTask的其他回调在UI线程上运行。 - Dheeraj Vepakomma
OnProgressUpdate和OnPostExecute。:-)) - Simon Dorociak
1
完全无关的是:Android自带一个HandlerThread类,因此将您的活动类命名与已存在的某些内容相同可能不是最佳选择。 :) - MH.
4个回答

32

为什么在安卓中要使用Handler?


第一步:了解什么是线程:

  • 线程有助于多任务处理
  • 线程可以看作是在主进程下运行的小型进程
  • 线程至少能呈现并行执行的外观

第二步:了解应用程序线程:

  • 当 Android 应用程序首次启动时,运行时系统将创建一个单独的主线程,此主线程将负责执行 Android 中的所有组件。

Android UI 工具包不是线程安全的

  • 如上所述,android 主线程中有许多组件,现在假设其中一个组件执行时间很长,则会使主线程 无响应,并且它将显示应用程序无响应
  • 子线程不能直接操作 Android 的(主)线程
  • 处理程序充当接口,从子线程收集消息,并随着消息到达逐个更新主应用程序线程,线程处理程序是在主线程中实现的。

Handler 类:

  • 为了实现多线程,我们将使用来自包 android.os.Handler 的 handler 类
  • 每个线程由一个 handler 类的实例处理

Figure

  • 从上图中我们可以看到,每个线程都由 Handler 类的一个实例处理
  • 线程之间通过消息进行通信
  • 此 handler 类有助于通过允许它们一起运行来实现多线程的同步协调

创建 handler 实例

Handler handlerObject = new Handler();

使用处理程序的最后一步是使用Runnable接口:

  • 处理程序类利用Runnable接口实现多线程
  • 我们重写run方法来执行指定次数的线程

Class NameOfClass implements Runnable
{
    Public void run()
    {
        //Body of run method
    }
}

将所有内容整合起来

//Create handler in the thread it should be associated with 
//in this case the UI thread
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
    public void run() {
        while(running){
            //Do time consuming stuff

            //The handler schedules the new runnable on the UI thread
            handler.post(new Runnable() {
                //Ex.. using progressbar to set the pogress
                            //Updating the UI is done inside the Handler
            });
        }
    }
};
new Thread(runnable).start();

为什么在 new Runnable() 体中有两个 "Run() 方法?是否只能使用一个方法完成? - Marek
1
那么这个简单的解决方案怎么样:runOnUiThread(new Runnable() { public void run() { } }); - Marek
我还没有尝试过...但是你的答案看起来简单得多,确实! - Devrath
1
@Unicorn,我想更详细地了解异步任务以及它们之间的区别... - Satheesh
@ Satheesh ..... 点击此链接 ..... 这可能会给你所寻找的信息! - Devrath
据我所知,asyncTask 用于将工作委托给后台,而 handlers 则用于在线程之间传递信息。我的理解正确吗? - cyc115

19

根据View的文档::

在调用任何视图上的任何方法时,您必须始终处于UI线程。如果您正在其他线程上工作并希望从该线程更新视图的状态,则应使用Handler

在您的示例中,当您需要在ProgressDialog上调用dismiss()方法时,根据上述文档,您必须在UI线程中执行此操作。当实例化HandlerThread类时(可能在UI线程上),messageHandler被初始化为Handler的一个实例。

根据Handler的文档::

每个Handler实例与单个线程及其消息队列相关联。创建新的Handler时,它将绑定到创建它的线程/消息队列-从那时起,它将向该消息队列传递消息和可运行对象,并在它们出队列时执行它们。

因此,要从新线程与UI线程通信,只需向在UI线程上创建的Handler发布消息即可。

如果您从UI线程外部调用View的方法,则会引发未定义行为,这意味着它可能看起来正常工作。但是它并不总是保证正常工作。


6
越简单越好。 你可以尝试使用以下代码,而不是使用处理程序:
runOnUiThread(
    new Runnable() { 
        public void run() 
        { 

        //Update user interface here

        } 
    }
);

不要让生活变得太复杂 ;)

4
如果你想要在UI线程中发布内容,你需要一个对活动的引用;而使用处理程序,则只需要处理程序本身。如果你只想保留视图引用,另一种解决方案是使用View.post(Runnable action)。 - Neron T

2

当一个应用程序启动时,Android系统会启动一个主线程的进程,负责处理UI渲染和事件。Android UI不是线程安全的,因此我们只能通过事件线程访问Android UI。在您的程序中,您定义了另一个线程而不是事件线程,如下所示:

 new Thread() {
        public void run() {
            try {

                Thread.sleep(8000);

                } catch (InterruptedException e) {

                }
                  messageHandler.sendEmptyMessage(0);

                }
    }.start();

现在,如果您想要关闭进度对话框,只能在事件线程中进行。Handler用于处理/处理消息队列中的消息。Handler与线程相关联,在您的情况下,它在事件线程中,因为默认情况下它会关联创建它的线程。通过messageHandler.sendEmptyMessage(),另一个线程向handler发送消息,handler会在handleMessage方法中处理此消息。


如果某个东西是或不是线程安全,这是什么意思?您能在您的回答中解释一下吗? - Marek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接