run on GL thread ?

27 views
Skip to first unread message

Andres Q

unread,
Feb 20, 2019, 9:35:30 AM2/20/19
to pl...@googlegroups.com
Hi,

I have set up admob on the game and it works fine, I want to show
something when the users gets a reward for viewing a video, so I
implement RewardedVideoAdListener on the Android Activity (so far,
everything standard).

The problem is that the callbacks are being made from the main thread,
hence if I want to display something on the UI it fails.

I tried adding plat.exec().invokeLater(...) or runOnUIThread(...) but
the runnable actions within are always called from the main thread!

I don't know if this is a bug or if I'm doing something wrong.

I was able to work around it by doing this:

@Override
public void onRewarded(RewardItem rewardItem) {
saveReward();
showReward = true;
}

@Override
public void onRewardedVideoAdClosed() {
if (showReward) {
showReward();
showReward = false;
}

// Load the next rewarded video ad.
...
}

and on my game screen:

public void showReward() {
game().paint.connect(clock -> {
// interact with TriplePlay UI and show a popup
}).once();
}

It works, but it seems like I'm doing an overkill, although I couldn't
find another way... thoughts?

Thanks

Michael Bayne

unread,
Feb 20, 2019, 1:40:25 PM2/20/19
to pl...@googlegroups.com
plat.exec().invokeLater(...) is what you should be calling to run code on the game thread. If that's not working, then something else is broken.

--

---
You received this message because you are subscribed to the Google Groups "PlayN" group.
To unsubscribe from this group and stop receiving emails from it, send an email to playn+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Michael Bayne

unread,
Feb 20, 2019, 1:46:04 PM2/20/19
to pl...@googlegroups.com
Though what you're doing is not wrong, so I suppose I shouldn't say that you *should* use exec().invokeLater(), just that exec().invokeLater() should work exactly like what you're doing.

The "simplest" way is to do game().plat.connect(plat -> /* do thing on game thread */).once();

The Game.paint signal is derived from the Platform.frame signal under the hood, and invokeLater() is just a Runnable queue that is also processed based on the Platform.frame signal. So the Platform.frame signal is the most basic way to get on the game thread. The different backends just emit a Platform.frame signal once per frame on the correct thread, and that's how everything else in the game is run.
--

Andres Q

unread,
Feb 20, 2019, 1:47:29 PM2/20/19
to pl...@googlegroups.com
I was using exec().invokeLater(), but it kept running the code from
the main thread (instead the GL thread) which broke things.

I'm looking more into it, to see if I can pinpoint why it's happening

El mié., 20 de feb. de 2019 a la(s) 15:46, Michael Bayne
(m...@samskivert.com) escribió:

Andres Q

unread,
Feb 20, 2019, 2:02:21 PM2/20/19
to pl...@googlegroups.com
Diagnostic is:

admob callback is called on main thread, on it I run:

platform().exec().invokeLater(() -> {
doSomething();
});

AndroidExec.invokeLater basically does:

if (isPaused()) activity.runOnUiThread(action);
else super.invokeLater(action);

in my case (called from admob callback) isPaused() evaluates to TRUE,
yet the Runnable is being called in the main thread!

Activity.runOnUiThread does:

public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

I wasn't able to debug into this method since I don't have the sources
or some misconfiguration in Android Studio, however since it's being
ran on the main thread I assume that it evaluates
Thread.currentThread() != mUiThread as FALSE

I'm guessing the problem is that isPaused is evaluated to true
(because the admob view for the video reward ad is on top of the game
view, I assume) and maybe that is causing problems?

El mié., 20 de feb. de 2019 a la(s) 15:47, Andres Q
(tuls...@gmail.com) escribió:

Michael Bayne

unread,
Feb 20, 2019, 2:02:40 PM2/20/19
to pl...@googlegroups.com
I think you were probably running into a problem with Android's handling of being paused. Look at AndroidExec.java:

  @Override public void invokeLater (Runnable action) {
    // if we're paused, we need to run these on the main app thread instead of queueing them up for
    // processing on the run queue, because the run queue isn't processed while we're paused; the
    // main thread will ensure they're run serially, but also that they don't linger until the next
    // time the app is resumed (if that happens at all)
    if (isPaused()) activity.runOnUiThread(action);
    else super.invokeLater(action);
  }

So if you're calling invokeLater() while the main game screen is not being shown, then your runnable will end up queued on the main thread rather than the game thread.

I'm not sure what a great solution is here. I don't remember exactly why I added the code in AndroidExec to shift to the main thread, but I think I was running into problems where runnables were getting queued up as the game was being hidden and then they never ran because the game loop was not running.


Michael Bayne

unread,
Feb 20, 2019, 2:06:58 PM2/20/19
to pl...@googlegroups.com
Yeah. I think we both found the problem at the same time.

I'm not sure if there's a great solution. Is it going to cause more problems for things to not run when someone expects them to run, or for them to run on the main thread when someone expects them to run on the game thread.

Maybe I'll just document Exec.invokeLater() as making sure that your runnable runs, which usually means running on the game thread, but can mean running on the OS UI thread in some cases. I could also add a specific invokeNextFrame() or something that always runs on the game thread, and document that runnables posted there are *always* run on the next game frame, even if that means the next frame happens a really long time from now because the game gets paused during this frame.

Andres Q

unread,
Feb 20, 2019, 2:08:58 PM2/20/19
to pl...@googlegroups.com
Perfect. I'll keep using the paint.connect(lambda).once() in the
meantime. I was hoping to catch a bug, but no luck :)

El mié., 20 de feb. de 2019 a la(s) 16:06, Michael Bayne
(m...@samskivert.com) escribió:

Michael Bayne

unread,
Feb 20, 2019, 2:10:06 PM2/20/19
to pl...@googlegroups.com

Sort of a bug. Certainly a confusion interaction of features that can hopefully be made less confusing.

Guillermo Rodriguez Garcia

unread,
Feb 22, 2019, 4:02:09 AM2/22/19
to pl...@googlegroups.com
Hi,

I am probably a bit late to the party, but is this a good idea?

Exec.invokeLater was documented to run stuff on the game thread, even though it sometimes didn't do that in Android. But it did behave as documented for other platforms.

So rather than "fixing the API spec", which implies modifying the RoboVM implementation, and thus also risks breaking apps relying on the documented behaviour, wouldn't it make more sense to modify invokeLater for Android so that it does what it is advertised to do, and then add a new API for running things "on the game thread or on the OS UI thread"?

Best,

Guillermo
Guillermo Rodriguez Garcia
guille.r...@gmail.com

Michael Bayne

unread,
Feb 28, 2019, 2:10:13 PM2/28/19
to pl...@googlegroups.com
I don't disagree with your perspective, but fixing invokeLater would potentially break Android apps relying on the old behavior. I know I was specifically relying on invokeLater code getting run when the app was paused so that I could sync data to the server and ensure that things were saved to Storage at that time. If that code all of a sudden did not get run, and then the app gets unloaded because it's in the background and the user does other stuff, then important code wouldn't get run.

So there's no "not going to break anything" option. Either I potentially break Android apps or I potentially break RoboVM apps. Technically I could just leave invokeLater with different behavior on different platforms, but I think that's not necessary given the low chance that this will break RoboVM apps. It would be somewhat strange to queue up a UI action just before an app was paused and then rely on it getting delayed until the app is resumed. Not impossible and could happen in rare cases like if the user started some delayed operation and then paused the app at just the right time, but it would be a rare race condition, not something that happened normally, like the Android app which queues up delayed actions when paused.

Anyhow, this is all pretty edge-casey, so hopefully it won't cause problems for anyone.
Reply all
Reply to author
Forward
0 new messages