我的应用程序将在其自己的线程上运行的SurfaceView与在主应用程序线程上运行的一堆常规视图相结合。在大多数情况下,这很好。但是,当我尝试在SurfaceView的线程上有一些东西触发更改主应用程序线程中的一个UI元素时,我得到android.View.ViewRoot $ CalledFromWrongThreadException。
有没有正确的方法?我应该使用asynctask吗? Runonuithread()?或者将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?
}
}
}
答案 0 :(得分: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
}
});
答案 1 :(得分:1)
我应该使用asynctask吗? Runonuithread()?
其中任何一个都应该没问题。对于在执行后台处理后需要更新AsyncTask
的大多数内容,我通常会使用UI
。但要么应该工作。
答案 2 :(得分:1)
为什么不使用回叫?在MySurfaceView中,您可以添加一个界面
public interface onClickSurfaceView{
public void change();
}
然后在您的活动工具MySurfaceView.onClickSurfaceView
中,您可以引用surfaceView
。然后调用surfaceView.setListener(this)
将活动注册为订阅者,
您可以在change()方法中调用更新您的UI。
答案 3 :(得分:0)
简单的答案是您无法从非UI线程更新UI元素。
你需要有一个回调方法或者回调主线程来更新UI的东西
答案 4 :(得分:0)
你需要使用它 -
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Populate UI
}
});