CalledFromWrongThreadException: 只有创建视图层次结构的原始线程才能触摸视图。

52

我在Android中遇到了以下错误:

CalledFromWrongThreadException;:只有创建视图层次结构的原始线程可以触摸其视图

当我尝试更新Activity中的Textview时,似乎会出现这个错误。虽然我从Activity内部调用更新TextView,但仍会出现上述错误。

我的代码如下:

onCreate() - 设置按钮和TextView。

onStateChange() - 监听有关状态更改的通知,当收到通知时,它将TextView更改为一些不同的文本。

当我收到新文本的通知时,我尝试更改TextView:

((TextView)findViewById(R.id.title)).setText("Some Text");

但我收到了上述错误。

从谷歌搜索结果来看,似乎应该使用处理程序来更改TextView或者可能使用AsyncTask?

有人能解释一下哪一个更好用以及为什么吗?

编辑:添加代码片段:


     public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);  

            setContentView(R.layout.my);

            getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.my_title);  

            ((TextView)findViewById(R.id.time)).setText("Hello Text");


            findViewById(R.id.keyboardimage).setOnClickListener(new OnClickListener() {
                public void onClick(View v) {

                    Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:"));
                    startActivity(dialIntent);

                        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.FLAG_SOFT_KEYBOARD));
                        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));       
                }
        });

     }

//CallBacks from running Service

private final ICallDialogActivity.Stub iCallDialogActivity = new ICallDialogActivity.Stub(){

@Override
public void onStateChanged(int callState)
                throws RemoteException {    
            switch(callState){
            case GlobalData.CALL_STATUS_IDLE:

                break;

            case GlobalData.CALL_STATUS_DISCONNECTING:
                byeSetup();
                break;
    } 

};

public void byeSetup(){

            ((TextView)findViewById(R.id.time)).setText("Bye Text");

            findViewById(R.id.keyboardimage).setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    //Void the Button
                }});
}

1
你在一个子类里吗?onStateChange被覆盖在哪里? - Pentium10
嗨Pentium10,它是通过AIDL接口覆盖并与服务通信的,服务将收到某些更改的通知,然后会告诉Activity根据此更新其TextView。我已添加了代码片段以更好地演示我的尝试。错误似乎非常随机,并且在Activity在前台和不在前台时都会出现。 - Donal Rafferty
1
可能是CalledFromWrongThreadException的重复问题。 - jww
6个回答

82
看起来你在错误的线程上。尝试使用Handler在正确的线程上更新GUI。请参考android.com的在UI线程处理昂贵操作示例。基本上,你需要用一个Runnable包装byeSetup,并通过一个Handler实例调用它。
Handler refresh = new Handler(Looper.getMainLooper());
refresh.post(new Runnable() {
    public void run()
    {
        byeSetup();
    }
});

6
看起来我不必自杀。松了一口气 - Jonas Gröger
我认为Keshav的答案更好。 - happyvirus
这比使用 runOnUiThread() 更好吗?我可以看到我们不需要对 Activity 的引用。这种解决方案还有其他优点吗? - Thomas

7

当变化涉及到主线程(UiThread)时,请在另一个线程中使用它来更改任何视图。

runOnUiThread(new Runnable() {
    @Override
    public void run() {

      // TODO your Code 
        et_Pass.setText("");
    }
});

1
如果您正在使用Fragment,那么可以通过getActivity().runOnUiThread()调用此方法,这应该是可接受的答案。 - Ashana.Jackol

3

为了更加清晰和易于实现,以下是我对willcodejavaforfood的回答进行扩展...

我已经让它工作了,以下是我的做法。我在一个服务中运行多个处理线程,因此其他在Activity中运行的解决方案无法使用,例如runOnUiThread(new Runnable() {}...

将以下代码放置在您的服务类的顶部,这样它就可以在该类的任何地方访问:

Handler handler;

将此代码放在您的服务类的onCreate方法或某个在服务主线程上加载的位置。
请注意,这是一句指令性语句,不需要翻译成解释性语句。
handler= new Handler(Looper.getMainLooper());

将以下代码放入您的附加线程中,以在UI或服务UI(无论它被称为什么)中运行:

handler.post(new Runnable() {
    public void run() {
        playNext(); //or whatever method you want to call thats currently not working
    }
});

2

对于其他人,只需将byeSetup()替换为您的代码语句或方法。byeSetup()是一个示例方法。希望这能节省您一些时间。


1
另一种方法是利用android.os.Message。在您的活动中将android.os.Handler定义为字段:
private final Handler myTextHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message stringMessage) {
        textView.append((String) stringMessage.obj);
        return true;
    }
});

然后像这样从你的另一个线程中提供它:
Message stringMessage = Message.obtain(myTextHandler);
stringMessage.obj = "Hello!";
stringMessage.sendToTarget();

0

你可以使用视图的内置post方法来更新其他线程中的内容,就像我在Kotlin中使用EditText一样。

address_box.post { address_box.text="my text"}

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