为啥在 runOnUiThread 做同样的事情时使用处理程序?

Posted

技术标签:

【中文标题】为啥在 runOnUiThread 做同样的事情时使用处理程序?【英文标题】:Why to use Handlers while runOnUiThread does the same?为什么在 runOnUiThread 做同样的事情时使用处理程序? 【发布时间】:2012-09-19 01:00:02 【问题描述】:

我遇到过Handlers 和runOnUiThread 的概念。但对我来说,他们在哪些事实上完全不同似乎仍然是一个疑问。

它们都旨在从后台线程执行 UI 操作。但是,我们在这两种方法中进行选择时要考虑哪些因素。

例如考虑Runnable Thread 在后台执行Web 服务,现在我想更新UI。

更新我的用户界面的最佳方式是什么?我应该选择Handler 还是runOnUiThread

我仍然知道我可以使用AsyncTask 并使用onPostExecute。但我只想知道区别。

【问题讨论】:

runOnUiThread 只是将Runnable 发布到Handler 的快捷方式。 Handlerandroid 定义的每个(?)跨线程通信工具的基础(例如,AsyncTaskonPostExecute 使用 Handler 来传递来自 doInBackground 的结果)。 【参考方案1】:

Activity.runOnUiThread() 是更通用的Handlers 的特例。使用Handler,您可以在自己的线程中创建自己的事件查询。使用通过 default constructor 实例化的 Handlers并不通常意味着“代码将在 UI 线程上运行”。默认情况下,处理程序绑定到实例化它们的Thread

要创建一个保证绑定到 UI(主)线程的 Handler,您应该创建一个绑定到 Main LooperHandler 对象,如下所示:

Handler mHandler = new Handler(Looper.getMainLooper());

此外,如果您检查runOnUiThread() 方法的实现,它正在使用Handler 来做这些事情:

  public final void runOnUiThread(Runnable action) 
        if (Thread.currentThread() != mUiThread) 
            mHandler.post(action);
         else 
            action.run();
        
    

从上面的代码 sn-p 可以看出,如果从 UI 线程调用 runOnUiThread()Runnable action 将立即执行。否则,它将发布到Handler,稍后将执行。

【讨论】:

但不是“实例化它们的线程”。通常是 UI 线程? @Mike,这完全取决于您的代码。通常是的,处理程序是从主线程实例化的,但是在很多情况下,开发人员在创建Handler 实例时没有关于当前线程的确切信息。因此,如果开发人员需要保证处理程序将在主线程中执行,则应使用new Handler(Looper.getMainLooper()) @Hit 非常感谢,你的Looper.getMainLooper() 短语在我遇到的一个毫无意义的问题上给了我很大的帮助,(***.com/questions/22831612/…) 另请注意,如果从 UI 线程调用 runOnUiThread()runOnUiThread() 将立即运行 action。否则,它会将其发布到 Handler。【参考方案2】:

处理程序是做事的旧方式(API 级别 1),然后引入了 AsycTask(API 级别 3),同时更加注重使用 runOnUIThread(API 级别 1)。您应该尽可能避免使用处理程序,并根据您的需要选择其他两个。

【讨论】:

但是为什么呢?我需要确切的区别。你能解释一下吗? 没有区别,你可以用 Handlers 和 Loopers 实现同样的事情,但是这些技术可以帮助你避免错误。见en.wikipedia.org/wiki/Syntactic_sugar【参考方案3】:

处理程序有许多工作,例如 消息传递 和频繁的 UI 更新,如果您为任何正在运行的任务启动线程。处理程序允许您发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象, ,这在许多应用程序中非常有用,例如蓝牙聊天、wifi 聊天……并且处理程序具有 PostDelay 和 PostAtTime 方法,您可以通过它们播放任何视图以设置动画和更改可见性等

你必须看看这个

http://developer.android.com/guide/components/processes-and-threads.html

http://developer.android.com/tools/testing/activity_testing.html

【讨论】:

runOnUIThread 总是在 UI 线程上进行计算,而 Using Handler U 可以触发一个线程在那里进行繁重的计算并将结果发布到使用 Handler 的 UI 线程。因此,如果您使用 runOnUIThread ,请注意不要对其进行大量计算。Async 内部也使用 Hanlder 来发布更新和进度。它最好取决于你的易用性。【参考方案4】:

按照 HitOdessit 的回答。

你可以像这样创建一个类。

public class Global
    private static Handler mHandler = new Handler(Looper.getMainLooper());
    public static void runOnUiThread(Runnable action)
        mHandler.post(action);
    

然后这样称呼它。

Global.runOnUiThread(new Runnable()
    //Your code
);

这可以从任何地方运行(你可以访问你的 Global 类的地方)。

【讨论】:

这只是语法糖。恕我直言,没有必要将处理程序的对象设为私有,甚至根本不需要在不同的类中。【参考方案5】:

更新 UI 的最佳方式是什么?我应该选择 Handler 还是 runOnUiThread?

如果您的 Runnable 需要更新 UI,请将其发布到 runOnUiThread

但并不总是可以在 UI 线程上发布 Runnable

想想场景,您需要执行 网络/IO 操作 或调用 Web 服务。在这种情况下,您不能将Runnable 发布到 UI 线程。它会抛出android.os.NetworkOnMainThreadException

这些类型的Runnable 应该在不同的线程上运行,例如HandlerThread。完成操作后,您可以使用已与 UI Thread 关联的Handler 将结果发送回 UI Thread。

public void onClick(View view) 

    // onClick on some UI control, perform Network or IO operation

    /* Create HandlerThread to run Network or IO operations */
    HandlerThread handlerThread = new HandlerThread("NetworkOperation");
    handlerThread.start();

    /* Create a Handler for HandlerThread to post Runnable object */
    Handler requestHandler = new Handler(handlerThread.getLooper());

   /* Create one Handler on UI Thread to process message posted by different thread */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) 
        @Override
        public void handleMessage(Message msg) 
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Runnable on HandlerThread is completed and got result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        
    ;

    NetworkRunnable r1 = new NetworkRunnable("http://www.google.com/",responseHandler);
    NetworkRunnable r2 = new NetworkRunnable("http://in.rediff.com/",responseHandler);
    requestHandler.post(r1);
    requestHandler.post(r2);



class NetworkRunnable implements Runnable
    String url;
    Handler uiHandler;

    public NetworkRunnable(String url,Handler uiHandler)
        this.url = url;
        this.uiHandler=uiHandler;
    
    public void run()
        try 
            Log.d("Runnable", "Before IO call");
            URL page = new URL(url);
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) 
                text.append(line + "\n");
            
            Log.d("Runnable", "After IO call:"+ text.toString());

            Message msg = new Message();

            msg.obj = text.toString();

            /* Send result back to UI Thread Handler */
            uiHandler.sendMessage(msg);


         catch (Exception err) 
            err.printStackTrace();
        
    

【讨论】:

以上是关于为啥在 runOnUiThread 做同样的事情时使用处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

当我们可以用 setter 做同样的事情时,为啥我们需要使用 builder 设计模式? [复制]

当按位运算符做同样的事情时,为啥要使用逻辑运算符?

如果无效元数据可以做同样的事情,为啥需要在 Impala 中刷新

为啥 shell=True 和 shell=False 做同样的事情? [复制]

为啥 HTTP/2 多路复用虽然 tcp 做同样的事情?

当 XG(跨组)事务可以做同样的工作时,为啥要选择实体组事务?