0

私のアプリは、独自のスレッドで実行されている SurfaceView と、メイン アプリケーション スレッドで実行されている一連の通常のビューを組み合わせています。ほとんどの場合、これで問題なく動作します。ただし、SurfaceView のスレッドで何かを実行して、メイン アプリケーション スレッドの UI 要素の 1 つを変更しようとすると、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?
        }
    }
}
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

コールバックを使用しないのはなぜですか? 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 に答える