Передайте переменные между рендерером и другим классом с помощью queueEvent () - PullRequest
11 голосов
/ 07 декабря 2011

Я хочу передать своему рендереру некоторые значения из другого класса.После того, как средство визуализации вычислило значения, у меня есть вспомогательный класс в классе помощника, который должен сообщить мне, что средство визуализации завершило вычисление, чтобы я мог продолжить работу с этими новыми значениями.Я могу передать средства визуализации без проблем, но я не могу понять, как их вернуть.В настоящее время я использую некоторые статические переменные, но после того, как они были изменены средством визуализации, они, похоже, теряются.Они не видны в моем другом классе.Пример:

A класс

public class View extends SurfaceView{

  private void doSomething(){

     glSurfaceView.queueEvent(new Runnable() {

                @Override
                public void run() {
                  //..
                  renderer.calculate(stack);    
                }
    });
  }

private void doAnotherThing(){

    //Never happens:
    if(Helper.hasCalculated){
    /...
    }
}

}

В моем рендерере :

public class MyRenderer implements GLSurfaceView.Renderer{

    private void calculate(Stack stack){         
      Helper.hasCalculated = true
    }
}

Мой вспомогательный класс:

public class Helper{

 public static volatile boolean hasCalculated = false;

}

hasCalculated определенно имеет значение true в рендере, но мой другой класс всегда видит его как false.Есть идеи почему?Мое лучшее предположение состоит в том, что это потому, что это в другой ветке, но как мне это решить?Если есть более чистый и безопасный подход, я был бы рад услышать его.

1 Ответ

14 голосов
/ 07 декабря 2011

Вы можете держать свой рендерер как переменную в своей деятельности (не просто mGLView.setRenderer(new MyRenderer());, как делают многие люди, а MyRenderer myRenderer = new MyRenderer(); mGLView.setRenderer(myRenderer);).Затем вы можете легко общаться со своим средством визуализации с помощью вызовов методов.Тогда проблема сводится к межпотоковой связи.Ниже я привел два примера, один из которых связан со связью между не-пользовательским потоком, GL-потоком и основным UI-потоком.Второй пример только для связи между потоком GL и потоком пользовательского интерфейса

public class Test3D extends Activity{

private MyRenderer renderer; // keep hold of the renderer as a variable in activity
private MyAsyncTask gameLoop;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    myRenderer = new MyRenderer(); // create the renderer object

    GLSurfaceView mGLView = (GLSurfaceView)findViewById(R.id.glsurfaceview1);
    mGLView.setEGLConfigChooser(true);
    mGLView.setRenderer(myRenderer); // set the surfaceView to use the renderer

    gameLoop = new MyAsyncTask(); 
    gameLoop.execute(); // start a new, non-UI, thread to do something

}

/// non-UI thread (inner class of my Test3D activity)
class MyAsyncTask extends AsyncTask<Void, Void, Void>{

    @Override
    protected Void doInBackground(Void... arg0) {

            myRenderer.startCalc(); // tell renderer to start calculation

            while(!myRenderer.isFinishedCalc()){

                // waiting for calc to finish, but not blocking UI thread

                try {
                    long x = 1000;
                    Thread.sleep(x);
                    // sleep the thread for x amount of time to save cpu cycles
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

            publishProgress(null); 
            // when calculation has finished, we will drop out of the loop
            // and update the UI



   }

    protected void onProgressUpdate(Void... progress) {         
        // update UI
    }


}


}

Затем в рендере

public class MyRenderer implements Renderer{

    private boolean startCalc = false;
    private boolean finishCalc = false;

    public void startCalc(){
        finishCalc = false;
        startCalc = true;
    }

    public boolean isFinishedCalc(){
        return finishCalc;
    }

    public void onDraw(GL10 gl){

        if(startCalc){
            // do calculation using GL handle
            // always performed in the GL thread

            finishCalc = true;
            startCalc = false;
        }

        // draw

    }



}

Я использовал флаги в приведенном выше примере рендерера, но это будетдовольно просто превратить это в очередь, если, скажем, вы хотели сказать рендереру «загрузить этот массив моделей».Поскольку вы должны загружать модели (или, по крайней мере, текстуры) в поток GL, используя дескриптор GL, вы можете сделать так, чтобы другие классы и потоки выполняли свою логику, и просто выполняли вещи GL в потоке GL



В качестве альтернативы, если вы просто хотите обновить поток пользовательского интерфейса после выполнения вычислений, а не взаимодействовать с любыми другими потоками:
public class MyRenderer implements Renderer{

    private Handler handler = null;
    public static final int CALC_FINISHED = 1;

    public void startCalc(Handler handler){
        this.handler = handler;
    }

    public void onDraw(GL10 gl){

        if(handler!=null){
            // do calculation using GL handle
            int flag = MyRenderer.CALC_FINISHED;
            handler.dispatchMessage(Message.obtain(handler, flag));
            // adds a message to the UI thread's message queue

            handler = null;

        }

        // draw

    }

}

и затем из любого места:

myRenderer.startCalc(new Handler(){

    public void handleMessage (Message msg){

        if(msg.what==MyRenderer.CALC_FINISHED){
            // Update UI
            // this code will always be executed in the UI thread

        }

    }

});
...