我如何从Android中的另一个线程操作主线程UI元素?
我的应用程序将运行在自己线程上的SurfaceView与运行在主应用程序线程上的一组常规视图相结合。在大多数情况下,这很好。然而,当我试图在SurfaceView的线程上触发对主应用程序线程中的一个UI元素的更改时,我得到了android.View.ViewRoot$calledFromErrorThreadException 有没有正确的方法?我应该使用异步任务吗?Runonuithread()?或者,将自己线程上的SurfaceView与主线程上的其他UI元素混合只是一件天生的坏事 如果我的问题没有意义,这里的伪代码可能更清晰我如何从Android中的另一个线程操作主线程UI元素?,android,multithreading,Android,Multithreading,我的应用程序将运行在自己线程上的SurfaceView与运行在主应用程序线程上的一组常规视图相结合。在大多数情况下,这很好。然而,当我试图在SurfaceView的线程上触发对主应用程序线程中的一个UI元素的更改时,我得到了android.View.ViewRoot$calledFromErrorThreadException 有没有正确的方法?我应该使用异步任务吗?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?
}
}
}
简单的答案是您不能从非UI线程更新UI元素
您需要有一个回调方法或一些回调主线程的东西来更新UI简单的答案是您不能从非UI线程更新UI元素
您需要有一个回调方法或调用主线程的东西来更新UI您需要使用这个-
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Populate UI
}
});
你需要使用这个-
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Populate UI
}
});
我应该使用异步任务吗?Runonuithread()
这两个都可以。我通常使用AsyncTask
来处理大多数需要在后台处理后更新UI
的事情。但两者都应该有效
及
我应该使用异步任务吗?Runonuithread()
这两个都可以。我通常使用AsyncTask
来处理大多数需要在后台处理后更新UI
的事情。但两者都应该有效
并且您不能从UI线程以外的其他线程更改UI元素。但是,您仍然可以从另一个线程向UI线程发布命令以更新元素。
您可以执行以下操作:
在UI线程中实例化处理程序,如:
final Handler handler = new Handler();
然后在另一个线程中,您可以向UI线程发布消息,通过处理程序更新视图,如下所示:
handler.post(new Runnable(){
public void run(){
//Update your view here
}
});
你应该是个好孩子。请记住,您必须在UI线程中实例化处理程序。
或
如果您正在
活动
实例中运行其他线程,则可以使用:
this.runOnUiThread(new Runnable(){
public void run(){
//your UI update code here
}
});
不能从UI线程以外的其他线程更改UI元素。但是,您仍然可以从另一个线程向UI线程发布命令以更新元素。
您可以执行以下操作:
在UI线程中实例化处理程序,如:
final Handler handler = new Handler();
然后在另一个线程中,您可以向UI线程发布消息,通过处理程序更新视图,如下所示:
handler.post(new Runnable(){
public void run(){
//Update your view here
}
});
你应该是个好孩子。请记住,您必须在UI线程中实例化处理程序。
或
如果您正在
活动
实例中运行其他线程,则可以使用:
this.runOnUiThread(new Runnable(){
public void run(){
//your UI update code here
}
});
你为什么不使用回调呢?在MySurfaceView中,您可以添加一个接口
public interface onClickSurfaceView{
public void change();
}
然后在您的活动中实现MySurfaceView.onClickSurfaceView
,您可以参考surfaceView
。然后调用surfaceView.setListener(this)
将活动注册为订户,
您可以在change()方法中调用update-your-UI。为什么不使用callback?在MySurfaceView中,您可以添加一个接口
public interface onClickSurfaceView{
public void change();
}
然后在您的活动中实现MySurfaceView.onClickSurfaceView
,您可以参考surfaceView
。然后调用surfaceView.setListener(this)
将活动注册为订户,
您可以在change()方法中调用update-your-UI。您能澄清一下它的位置吗?是否应该在MainActivity中创建它,然后mysurfaceview.draw()将调用什么?我想在这种情况下,
runOnUiThread()
可能更容易实现,也不太混乱。请参阅我的示例链接(刚刚更新了错误的链接),不客气。通常,当我需要更新UI
时,我会使用AsyncTask
进行后台操作,但在再次查看您所拥有的内容后,runOnUiThread()
可能会更容易。但是,如果您还不确定自己是否了解异步任务在未来的工作方式,请将其通读几遍……非常有帮助。您能澄清一下它将在哪里使用吗?是否应该在MainActivity中创建它,然后mysurfaceview.draw()将调用什么?我想在这种情况下,runOnUiThread()
可能更容易实现,也不太混乱。请参阅我的示例链接(刚刚更新了错误的链接),不客气。通常,当我需要更新UI
时,我会使用AsyncTask
进行后台操作,但在再次查看您所拥有的内容后,runOnUiThread()
可能会更容易。但是,如果你还不确定自己是否了解这对未来的工作方式,请将AsyncTask
文档通读几遍……非常有用。+1对于另一种方法-我是否正确地认为这将与rununuithread做的差不多,并且两者都可以工作?它们都做相同的事情,但是您可以在任何地方使用第一个选项,而第二个选项只能在活动
中使用,因为runOnUiThread()
方法仅在活动
类中声明,这两种方法都可以工作?它们都可以做相同的事情,但是您可以在任何地方使用第一个选项,而第二个选项只能在活动
中使用,因为runOnUiThread()
方法仅在活动
类中声明。