如何操作Android中另一个线程的主线程UI元素?

时间:2013-09-13 19:04:45

标签: android multithreading

我的应用程序将在其自己的线程上运行的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?
        }
    }
}

5 个答案:

答案 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。但要么应该工作。

AsyncTask Docs

runOnUiThread exampleDocs

答案 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

        }
    });
相关问题