winit icon indicating copy to clipboard operation
winit copied to clipboard

Android `RecreationAttempt` on Activity reopen

Open SnowyCoder opened this issue 1 year ago • 9 comments
trafficstars

When an android app calls event_loop.exit() the application exits, but if the application is opened again without closing it in the "recent apps" the EventLoop creation will fail with a RecreationAttempt error.

Steps to reproduce

  • clone https://github.com/SnowyCoder/winit-bug-reopen-example
  • run in an android phone
  • press back button
  • reopen app

Reported error

called `Result::unwrap()` on an `Err` value: RecreationAttempt

Repo explaination

The repo linked in the "Steps to reproduce" section is a modified version of the one linked in the README android section. I just updated the dependencies and linked BrowserBack to event_loop.exit() (that sems to be the logical key invoked when the android back button is pressed).

Comment

The general problem seems to be that android_main can be called more than once without unloading and reloading the native library (ex. if we use an application with two interacting activities, say LoginActivity and GameActivity).

SnowyCoder avatar Dec 26 '23 15:12 SnowyCoder

See https://github.com/rust-windowing/winit/pull/2344#issuecomment-1166662533, which points out how this can happen on Android.

But generally speaking this is the intended behavior by Winit, you just have to figure out how to avoid having to recreate the event loop.

Let us know if this solves your problem!

daxpedda avatar Dec 26 '23 16:12 daxpedda

I read that conversation and it doesn't really say how to solve the issue, I've tried various approaches but haven't yet had success. Is there any example Android app that has this bug solved?

Here are my attempts:

  1. First thread creates the event loop, saves it in a const and next threads use that loop. Cannot work since event loops cannot be shared across threads
  2. Since that conversation talks about not creating the loop in the fake main function, create a common thread that manages the event loop and when a new invocation occurs it calls into the common thread Does not work since the new thread does not have an attached looper
  3. Let the first thread create the event loop and keep it alive as a "common thread" like in the second approach, reusing that event loop for the next invocations. Since the main thread never exits the app stops responding (I'm blocking the UI thread?)

I think that the "best" approach would be to augment the second point, spawn a new common thread that manages the event loop and manually create a looper out of it. But it involves nasty low-level JNI calls and I'm not even sure if it would really work. If we let android recreate its event loop once it exits is, to me, the most elegant option. (tested it commenting the RecreationAttempt line, doesn't show any sign of error).

TL;DR: I tried, but I have no idea on how to get it working without recreating the event loop or calling JNI stuff.

SnowyCoder avatar Dec 26 '23 18:12 SnowyCoder

Per the linked comment, after previously muddling along with these issues in ndk-glue for a while, none of those design issues were addressed when android-activity was created :weary:

Blindly recreating the loop when a new activity is created seems appalling. Consider activities to be analogous to Windows. Your scenario concerns "closing" one activity (I think) and then later having a new activity opened. However, it is also possible to have multiple activities opened at the same time.

Fwiw I think exiting the loop and returning out of android_main() causes your activity to be closed because of https://github.com/rust-mobile/android-activity/issues/67 / https://github.com/rust-mobile/android-activity/pull/81. Also notice that the callback function from Android here is called ANativeActivity_onCreate() which is not analogous to something called main(), yet pretends to be used as such.


We'll need to be more thoughtful how to handle that across the winit and android-activity bindings. You'll see the ANativeActivity_onCreate() callback in android-activity is already creating a thread to run your android_main() and the event loop in. That's likely the thread that will have to stay alive to process incoming window creation requests through ANativeActivity_onCreate(), until the process exits.

Then, we'll likely need a "New Window Created" event in winit to process this (and make the Window impl for Android a less-empty shell), and also improve the Suspended/Resumed events to operate per-surface (should be a 1:1 mapping with an activity) instead of just globally.

This means that creating a new window in winit will no longer do what you expect, and we'll have to craft some careful bindings around it.

MarijnS95 avatar Jan 06 '24 16:01 MarijnS95

https://developer.android.com/guide/topics/manifest/activity-element#lmode may also be relevant here.

MarijnS95 avatar Jan 06 '24 16:01 MarijnS95

I'm experiencing this issue with OpenXR on the Meta Quest. Your thread explains well why Meta discourages exit by the application and asks to systematically use the "Meta" button which pauses and allows Resume or Quit. My problem is that not all other platforms have this button.

mvvvv avatar Apr 26 '24 15:04 mvvvv