0

我的应用程序结合了在自己的线程上运行的 SurfaceView 和在主应用程序线程上运行的一堆常规视图。在大多数情况下,这工作正常。但是,当我尝试让 SurfaceView 的线程上的某些内容触发对主应用程序线程中的 UI 元素之一的更改时,我得到了 android.View.ViewRoot$CalledFromWrongThreadException。

有没有解决这个问题的正确方法?我应该使用异步任务吗?运行线程()?或者将自己线程上的 SurfaceView 与主线程上的其他 UI 元素混合只是天生的坏事?

如果我的问题没有意义,这里的伪代码可能更清楚。

// Activity runs on main application thread
public class MainActivity extends Activity {
    RelativeLayout layout;
    TextView popup;
    MySurfaceView mysurfaceview;

    protected void onCreate(Bundle savedInstanceState) {
        ...
        setContentView(layout);
        layout.addView(mysurfaceview);  // Surface View is displayed
        popup.setText("You won");       // created but not displayed yet
    }
    public void showPopup() {
        layout.addView(popup);
    }
}

// Surface View runs on its own separate thread
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener {
    private ViewThread mThread;

    public boolean onTouch(View v, MotionEvent event) { 
        ...
        if (someCondition == true) {
            mainactivity.showPopup();
            // This works because onTouch() is called by main app thread
        }
    }
    public void Draw(Canvas canvas) { 
        ...
        if (someCondition == true) {
            mainactivity.showPopup();
            // This crashes with ViewRoot$CalledFromWrongThreadException
            // because Draw is called by mThread
            // Is this fixable?
        }
    }
}
4

5 回答 5

4

您不能从 UI 线程以外的其他线程更改 UI 元素。但是,您仍然可以从另一个线程向 UI 线程发布命令以更新元素。

您可以执行以下操作:

在 UI 线程中实例化一个处理程序,例如:

final Handler handler = new Handler();

然后在您的另一个线程中,您可以向 UI 线程发布消息以通过处理程序更新您的视图,如下所示:

handler.post(new Runnable(){

    public void run(){
        //Update your view here
    }
});

你应该很高兴去。请记住,您必须在 UI 线程中实例化处理程序。

或者

,如果您在Activity实例中运行其他线程,则可以使用:

this.runOnUiThread(new Runnable(){

     public void run(){
         //your UI update code here
     }
});
于 2013-09-13T19:17:33.793 回答
1

我应该使用异步任务吗?运行线程()?

这些都应该没问题。我通常AsyncTask用于大多数需要UI在执行后台进程后更新的事情。但任何一个都应该工作。

AsyncTask 文档

runOnUiThread 示例文档

于 2013-09-13T19:08:36.773 回答
1

为什么不使用回调?在 MySurfaceView 中,可以添加一个界面,

public interface onClickSurfaceView{
     public void change();
}

然后在您的活动实施MySurfaceView.onClickSurfaceView中,您可以参考surfaceView. 然后调用 将surfaceView.setListener(this)活动注册为订阅者,您可以在 change() 方法中调用更新您的 UI。

于 2015-07-20T12:35:11.603 回答
0

简单的答案是您不能从非 UI 线程更新 UI 元素。

您需要有一个回调方法或回调主线程的东西来更新 UI

于 2013-09-13T19:08:03.110 回答
0

你需要使用这个 -

runOnUiThread(new Runnable() {

        @Override
        public void run() {
            // TODO Populate UI

        }
    });
于 2013-09-13T19:08:32.963 回答