处理器改变UI导致CalledFromWrongThreadException [英] Handler changing UI causes CalledFromWrongThreadException

查看:179
本文介绍了处理器改变UI导致CalledFromWrongThreadException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个处理程序,可以从任何地方访问的活动中,也写了一个方法,使其更容易调用处理程序:

 专用处理器textFromBGThread =新的处理程序(){
    @覆盖
    公共无效的handleMessage(消息MSG){
        //从味精字符串
        字符串outputString = msg.getData()的getString(输出)。
        //查找的TextView
        TextView的输出=(的TextView)findViewById(R.id.C​​onsoleOutputView);
        //显示输出
        Log.i(TextOutput,关于以显示消息:+ outputString);
        Output.setText(Output.getText()+ outputString);
        Log.i(TextOutput,信息显示);
    }
};私人无效TextOutputWrapper(字符串的outputText){
    消息味精=新的Message();
    捆绑bndle =新包();
    bndle.putString(输出,\\ n+的outputText);
    msg.setData(bndle);
    textFromBGThread.handleMessage(MSG);
}

于是这可以从后台线程简单地称为

  TextOutputWrapper(Attemping连接...);

这将工作1+倍,然而,实际的视觉变化会引起 CalledFromWrongThreadException 抛出。作为对Java&安培; Android的,我卡在为什么发生这种情况。

我已经注意到,碰撞往往当有电话和放大器之间稍长的时间段发生的;如果以 TextOutputWrapper(字符串)的调用正在一个接一个很快发生,那么它的工作原理。例如,这

  INT I = 0;
而(ⅰ&所述; 200){
    TextOutputWrapper(将String.valueOf(I));
    我++;
}

正常工作。

说完看着LogCat中,似乎垃圾收集器释放一些资源,然后下被称为时间 TextOutputWrapper(字符串),它崩溃时( Output.SetText(字符串)被调用,以precise),虽然我不知道是什么原因,将导致此错误。


解决方案

有几件事情我想改变这里:

使用处理程序

A 处理程序如果要触发更新UI,并从非UI线程这样做是非常有用又名的背景线程)。

在你的情况,这不是服务于这个目的。您的直接呼叫

  textFromBGThread.handleMessage(MSG);

这不是专为你做到这一点。这就是你应该使用处理程序是实现你想要做的的handleMessage(消息)用户界面是什么方法。你这样做。但是,你不应该的直接的通话的handleMessage()。如果你这样做,那么的handleMessage()将被从任何线程调用名为 TextOutputWrapper()如果这是一个后台线程,那么这是错误的。

您想要做的是调用处理程序的<一个href=\"http://developer.android.com/reference/android/os/Handler.html#sendMessage%28android.os.Message%29\"相对=nofollow>的sendMessage(消息)方法(或其他可用的变种之一)。 的sendMessage()将会把你的信息在一个线程安全的队列,也就是那么主线程处理。然后,主线程将调用处理程序的的handleMessage(),传递回排队的消息,并允许它安全地更改UI。因此,改变 TextOutputWrapper()来使用:

 私人无效TextOutputWrapper(字符串的outputText){
    消息味精=新的Message();
    捆绑bndle =新包();
    bndle.putString(输出,\\ n+的outputText);
    msg.setData(bndle);
    textFromBGThread.sendMessage(MSG);
}

Java约定

这code是一个有点难以阅读,对于有经验的Java开发人员。在Java中,典型的编码标准保留大写的名字的东西像类,而与方法小写字母开始。所以,请方法重命名为:

 私人无效textOutputWrapper(字符串的outputText);

,或者更好的,因为这实际上是一种方法,而不是包装,本身的,重命名为类似

 私人无效的outputText(字符串文本);

安全线程替代

最后,我可能会建议,如果你只是想一个方法,可以让你安全地从任何线程修改UI,用另一种技术。我没有找到处理程序来是简单易用,适合初学者。

 私人无效的outputText(最后弦乐outputString){
    runOnUiThread(新的Runnable(){
        @覆盖
        公共无效的run(){
            //查找的TextView
            TextView的输出=(的TextView)findViewById(R.id.C​​onsoleOutputView);
            //显示输出
            Log.i(TextOutput,关于以显示消息:+ outputString);
            output.setText(Output.getText()+ outputString);
            Log.i(TextOutput,信息显示);
        }
    });
}

runOnUiThread()是每个活动可用的方法。

我会指示你一些普通文档在Android的理解线程:

http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html

http://android-developers.blogspot.com/2009/ 05 /无痛-threading.html

I've created a Handler that can be accessed from anywhere within the activity and also written a method to make it easier to call the handler:

private Handler textFromBGThread = new Handler() {
    @Override
    public void handleMessage (Message msg) {
        // Get the string from the msg
        String outputString = msg.getData().getString("Output");
        // Find the TextView
        TextView Output = (TextView)findViewById(R.id.ConsoleOutputView);
        // Display the output
        Log.i("TextOutput","About to display message: " + outputString);
        Output.setText(Output.getText() + outputString);
        Log.i("TextOutput","Message displayed");
    }
};

private void TextOutputWrapper (String outputText) {
    Message msg = new Message();
    Bundle bndle = new Bundle();
    bndle.putString("Output", "\n" + outputText);
    msg.setData(bndle);
    textFromBGThread.handleMessage(msg);
}

So then this can be called from a background thread simply with:

TextOutputWrapper("Attemping to connect...");

This will work 1+ times, however, the actual visual change will cause a CalledFromWrongThreadException to be thrown. Being new to Java & Android, I'm stuck on why this is happening.

I have noticed that the crash tends to happen when there's a slightly longer time period between calls & that if the calls to TextOutputWrapper(String) are happening very quickly after one another, then it works. For example, this:

int i = 0;
while (i < 200) {
    TextOutputWrapper(String.valueOf(i));
    i++;
}

works fine.

Having looked at LogCat, it seems that the garbage collector frees up some resources and then the next time TextOutputWrapper(String) is called, it crashes (when Output.SetText(String) is called, to be precise), although I'm not exactly sure why that would cause this error.

解决方案

There's a few things I'd change here:

Using Handler

A Handler is useful if you want to trigger the UI to update, and do so from a non-UI thread (aka "background" thread).

In your case, it's not serving that purpose. You are directly calling

textFromBGThread.handleMessage(msg);

It's not designed for you to do that. The way you are supposed to use Handler is to implement what you want done to the UI in the handleMessage(Message) method. You did that. But, you shouldn't directly call handleMessage(). If you do that, then handleMessage() will be called from whatever thread invokes TextOutputWrapper(). If that's a background thread, then that's wrong.

What you want to do is to call the handler's sendMessage(Message) method (or one of the other available variants). sendMessage() will put your message in a thread-safe queue, that is then processed on the main thread. The main thread will then invoke your handler's handleMessage(), passing it back the queued message, and allowing it to safely change the UI. So, change TextOutputWrapper() to use this:

private void TextOutputWrapper (String outputText) {
    Message msg = new Message();
    Bundle bndle = new Bundle();
    bndle.putString("Output", "\n" + outputText);
    msg.setData(bndle);
    textFromBGThread.sendMessage(msg);
}

Java Conventions

This code is a bit hard to read, for an experienced Java developer. In Java, typical coding standards reserve upper case names for things like classes, while methods start with lower case letters. So, please rename the method to:

private void textOutputWrapper (String outputText);

or, better yet, since this is in fact a method, and not a wrapper, per se, rename to something like

private void outputText(String text);

Safe Threading Alternatives

Finally, I might recommend that if you simply want a method that allows you to safely modify the UI from any thread, use another technique. I don't find Handler to be that easy to use for beginners.

private void outputText(final String outputString) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            // Find the TextView
            TextView output = (TextView)findViewById(R.id.ConsoleOutputView);
            // Display the output
            Log.i("TextOutput","About to display message: " + outputString);
            output.setText(Output.getText() + outputString);
            Log.i("TextOutput","Message displayed");
        }
    });
}

runOnUiThread() is a method available in every Activity.

I'll also point you to some general docs on understanding threading in Android:

http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html

http://android-developers.blogspot.com/2009/05/painless-threading.html

这篇关于处理器改变UI导致CalledFromWrongThreadException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆