0

Situation: Android

class GLRenderer implements GLSurfaceView.Renderer
    ..
    void onDrawFrame(GL10 gl) {..}

class MainGLSurfaceView extends GLSurfaceView
    ..
    setRenderer(new GLRenderer());

class MainActivity ..
    ..
    boolean onTouchEvent(MotionEvent event) {..}

MainActivity.onTouchEvent receives and processes events, some of which change state used by onDrawFrame(gl).

Q 1: Is there a way to "put the message queue on hold" until onDrawFrame returns?

NOTE: my onDrawFrame might take up to ~1/3 second on a slow phone. If necessary, I can probably change it so that if it doesn't have information it needs, it can start fetching it in the background, return promptly, and then draw the new frame on a later draw request (triggered by a timer).

Q 1B: Perhaps the events are only interrupting the draw, because of something I'm doing to fetch data. Can events interrupt at any moment (in the middle of onDrawFrame), or is it only when my custom onDrawFrame logic makes certain (system?) calls?

A 1B: Unfortunately, with a breakpoint I caught the event interrupting in the middle of a computation (a VM "init" of a new instance of a small class used to hold a temporary value was all that was required, to be "interruptible"; something almost any java code might do). So I will need to cope with interrupting events, can't side-step them.

Q 2: Or would it be better to examine incoming messages, and somehow decide which ones should be handled immediately, and then ?do what? with other messages, to process them after onDrawFrame returns?

Q 3: I've made an attempt at Q 2, putting messages on to an internal queue. Then I tried processing them at end of the onDrawFrame method. This worked okay until a message which tried to open a confirmation dialog. Result: RuntimeException: Can't create handler inside thread that has not called Looper.prepare(). Yeah, I didn't think I should be doing it that way. Can i somehow shove those messages back on to the main message queue?

(I didn't want to create yet another thread, so at end of onDrawFrame I tried "new Handler().postDelayed(new Runnable() .." inside of which I was going to do something with those events. Oops - that has the same problem - can't create handler on the thread that onDrawFrame is running on.)


So the basic idea is that I seek a way to not pull the rug out from under the current draw frame. I'd rather not have to make all the event-triggered logic work on one set of data, then "freeze" that data (copy it), so that draw frame can work on a frozen set.

Q&A's that I looked at before asking this:

Message queue in android

This talks about creating a looper and a handler, including a link to another article. I might adapt this to create a handler attached to the main thread's looper. So instead of having to inject back into the MessageQueue, I just need to pass my secondary queue to this handler, and start it running. But I'm on the wrong thread at the time I want to start the handler, so not sure how to proceed. Hmm, maybe make a custom event, that I somehow trigger on the main thread?

How to pause the activity?

shows how to use a flag to pause a worker thread. Since I have multiple types of events I wish to defer, instead of that approach, it is easier to hold (copies of) those events on a separate queue. Then I just need to know how to "inject" them back into the main MessageQueue. I'm trying to avoid creating another thread, to minimize system resources.

How to pause a Thread's Message Queue in Android?

Alternate approaches (not using looper) when creating one's own thread, e.g. a worker thread. Doesn't help for my situation, which is UI events coming in to existing looper.


And in case there is some completely different way to tackle this:

isn't this a situation that everyone who uses GLSurfaceView rendering would encounter eventually?

Is there any example of a robust way to deal with gl drawing and asynchronous GUI events?

2

1 Answer 1

0

The final piece to my solution to "Q 3":

public class MainActivity ...

    // Call this, if not already on UI thread.
    public static void processQueuedEventsOnUIThread() {
        try {
             Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    ... process the deferred UI events, which I have stored on a private queue ...
                }
            };
            MainActivity.mMainActivity.runOnUiThread(runnable);
        } catch (Exception e) {
            Log.e("MainActivity", "processQueuedEventsOnUIThread", e);
        }
    }

The last statement in my GLRenderer.onDrawFrame() is now

MainActivity.processQueuedEventsOnUIThread();

The exceptions no longer occur, even if the processed events cause a dialog window (with its own handler) to open. activity.runOnUiThread(runnable) is the essential step.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.