Qt World Summit: Submit your Presentation


Android: E Qt JAVA : Surface 1 not found!


  • Qt Champions 2016

    as you probably know I wrote some Qt Blogs and developed ‘ekkes Share Example App’ to demo How to share to and from other Apps.
    I run into a problem on Android sharing data from other Apps
    Have defined App as ‘singleInstance’ in Android Manifest, because I only want to use one Instance of my Qt mobile App.

    Reason: some customer apps have a complex Login / Init workflow, so I always want to re-use the App if already running. Also using ‘singleInstance’ if my already running App was opened by another App my App shows exactly the same Page I just worked with and in some scenarios the incoming data should be inserted there.

    Opening my App from other Apps in most cases works perfect:

    • if my App was not started, the app will be started by external Intent. This Intent was delivered via public void onCreate(...)
      of my Qt Activity. I wait until Qt UI was initialized and then process the pending Intent.
    • if my App already was started, the external App comes in via public void onNewIntent(Intent intent) and I can process the Intent to get the data

    Not all Android Apps are goot citizens. There are some Apps out there not using the public void onNewIntent(Intent intent) if my App already was running. They come in via public void onCreate(...) and now Qt comes in trouble because the Activity already was created and started. I’m getting a white screen, App freezes and this Error was logged:

    E Qt JAVA : Surface 1 not found!
    

    only way to go on was to kill the App.

    So I tried to come out of this trap. Inside public void onCreate(...) I verified that the Activity knows that the App was running. Also I tried getIntent() and was able to process the Intent, to receive and save the data. But cannot go on because UI is freezed with white screen.
    Now I tried to call finish() and return. This avoids the white screen, but exits the App.

    Better then freezing ;-)

    I also tried onBackPressed() - but didn’t work.

    Does someone has an idea how to get back the control if public void onCreate(...) of Qt Activity was called a 2nd time ?

    Want to get rid of white screen and surface null error.

    One workaround could be to store the incoming data from Intent into a special folder inside app data and check this after restarting the app again and ask user if the data from last Intent should be used.



  • I'm fighting with this too currently and I think I've found the most dirty workaround that seems to be working:

    After your hints to call finish() and return to at least prevent the surface from freezing, I tried to relaunch myself again by launching a new, intent:

        private static MyAppActivity instance = null;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (instance != null) {
                Log.d(TAG, "App is already running... this won't work");
                Intent intent = getIntent();
                if (intent != null) {
                    Log.d(TAG, "There's an intent waiting...");
                    String sharedData = processViewIntent(intent);
                    if (sharedData != null) {
                        Log.d(TAG, "It's a view intent");
                        Intent viewIntent = new Intent(getApplicationContext(), MyAppActivity.class);
                        viewIntent.setAction(Intent.ACTION_VIEW);
                        viewIntent.putExtra("sharedData", sharedData);
                        startActivity(viewIntent);
                    }
                }
                finish();
                return;
            }
            instance = this;
            Log.d(TAG, "Android activity created");
        }
    

    So, how it works:
    When the app is launched normally, a static instance pointer will keep a reference to the first created activity. This is only to determine whether we're launched normally or onCreate() was called a second time because of those weird ACTION_VIEW intents.

    If the code detects that we're launching a second time, it fetches the waiting intent and processes it by reading the data from the content URL. This needs to happen in this run because after the app restart the app won't have permissions any more to still read it.

    So, once we've read the content data, we'll be putting it into a new Intent as StringExtra and relaunch ourselves with that intent instead of the original one.

    The processViewIntent() code now first checks if the launching intent already has the "shareData" as string extra and uses that if available. If not, it continues the other route of actually reading the content data from the data URL.

    Interestingly, in the logs I can see that this will actually fire the intent in a loop, i.e. onCreate() is called again before finish() actually finishes our activity. This doesn't seem to be a problem as eventually the finish() will close the app and then restarts it and all works fine.

    As I said, probably one of the dirtiest workarounds ever, but I couldn't figure out anything better after numerous hours of fighting this issue.

    Hope it helps someone, and if you find out a better solution, please do let me know.


  • Qt Champions 2016

    @mzanetti1 thanks ! - dirty workaround is better then no workaround ;-)
    will take a look at this later (can be weeks) - just overloaded by work on customer mobile apps and also preparing my apps for Qt6



  • I should probably add that the StringExtra bit only works if the shared data is small, as it is in my case. If importing bigger files, then I suppose the processViewIntent() would need to copy the file into the apps cache in one way or another, as ekke already said. But the self-restarting bit through firing a new Intent should be applicable in any case.


Log in to reply