Need Help with Mobile App Crashes

Hello everyone,

I have had an iOS and Android application available on the app stores (iOS and Android) for over a year now. While I’m not a C++ expert, I am quite satisfied with the overall result of my application. It’s a comprehensive application, but unfortunately, around 3% of users are experiencing crashes (ANR or app crashes). I use Firebase Crashlytics to track issues. This percentage is too high. The crashes occur either within the JUCE library or, occasionally (0.4% of the time), during the display of an Admob ad. I’ve attempted to share the details of these issues on the forum in the past, but no one has been able to assist me.

I’m curious if there are any JUCE (Full, including the JUCE interface) users on iOS and Android who have encountered very few bugs. Regrettably, I lack the expertise to understand all the intricacies of JUCE’s code, and I can’t reproduce these bugs on my own devices.

In general, it appears that these crashes may be related to overlaying pages, displaying a web page on top, showing an ad on top, returning to my app, cleanly closing my app, and managing threads, among other things.

My JUCE application interface (JUCEApplication) is standard, but there are instances when I need to display a web page (e.g., Terms Of Use or Privacy Policy) or a store page (Appstore Connect, pay or PlayStore Rate And Review), share pages (audio or midi), or show an ad (Google Admob), and my crashes occur during these moments. Therefore, it’s possible that the issue lies in how my application launches (main.cpp) or in managing the suspended() or resumed() states. I don’t know.

Understanding the Firebase Crashlytics reports is challenging because these issues seem to originate outside of my code.

If anyone can provide guidance, advice, or any help that can help me improve and finally have a stable application, it would be greatly appreciated. I would also be grateful for the opportunity to connect with someone who has a comprehensive application running smoothly on both iOS and Android.

I understand that many JUCE users primarily focus on creating plugins rather than smartphone applications, but I’m hopeful that I can find some assistance. I’m also aware that there are individuals who use JUCE on smartphones, primarily for audio processing, and they may utilize native iOS and Android interfaces for visual components.

I’m more than willing to share whatever is necessary, be it code, Crashlytics data, or anything else that could be of help.

Thank you very much,
ToBy

Or perhaps my issue lies in the foundation of my sequencer with its ‘waitForMillisecondCounter’ calls.

My main sequencer is a ‘HighResolutionTimer.’ In the ‘hiResTimerCallback,’ I send MIDI note events to my ‘AudioSource’ components (using the JUCE ‘Synthesiser’ in my ‘AudioSource’). In this process, I use ‘waitForMillisecondCounter’ to introduce groove or prevent clicking during note overlaps.

It’s possible that when a user interacts with my app, such as clicking a button to display a web page, store, ad, etc., during a ‘waitForMillisecondCounter’ call, it may lead to crashes. I’m not entirely certain. Or perhaps it’s essential to ensure that everything is properly finalized, waits, music, etc., before transitioning my app to the background?

Problems like these are sometimes (often!) caused by memory corruption or threading issues, or a combination of both. If possible, you can build and test your app with the Clang address sanitizer or thread sanitizer. In Xcode it should be as simple as enabling one of the corresponding checkboxes on the “Diagnostics” tab of the “Scheme Editor”. There are serious performance impacts of enabling these so you should only enable them for your test builds.

As a side note, when sending midi into an audio stream, you don’t want to use HighResolutionTimer
but compute the time using the buffer from the audio thread

@otristan
I’m not entirely sure I understand what ‘compute the time using the buffer from the audio thread’ means. Does it imply that I should use ‘getNextAudioBlock()’ to replace my timer? Would I count the number of times the ‘getNextAudioBlock()’ function is called (based on the sample rate), and from that, deduce a BPM, etc? I might not have grasped the concept completely.
I’ve been searching for a JUCE-based sequencer on GitHub that uses this concept of ‘compute the time using the buffer from the audio thread,’ but unfortunately, I haven’t found one yet.

Thank you very much for your help!

Yes this is how it should be done
You can check the arpeggiator example
https://docs.juce.com/master/tutorial_plugin_examples.html#tutorial_plugin_examples_arpeggiator
or this

Google’s Ad SDKs have always been very buggy, so some of the crashes might simply be due to that. (In that case, the stacktrace would show a crash in the code for the Admob SDK.)

@kerfuffle
Yes, there are indeed some issues with the Admob SDK, and I’ve encountered a few of them:

  • firebase::gma::GmaInternal::CompleteLoadAdFutureFailure Resulting in ‘EXC_BAD_ACCESS (KERN_INVALID_ADDRESS)’
  • firebase::gma::internal::InterstitialAdInternalIOS::InterstitialDidReceiveAd(GADInterstitialAd*) Also leading to ‘EXC_BAD_ACCESS (KERN_INVALID_ADDRESS)’
  • firebase::FutureHandle::FutureHandle(firebase::FutureHandle const&) Which triggers an ‘EXC_BAD_ACCESS (KERN_INVALID_ADDRESS)’ error."

But are these issues a result of my usage of the Admob SDK, or is there nothing I can do to prevent them?

Thank you very much for your response

@otristan
Thank you so much for these examples; I understand better now. Unfortunately, I wish I had come across this tutorial before building my app. I should have done more research. I have a lot of changes to make in my application to implement this properly, but it makes much more sense than what I’ve done.

Do you think there’s a chance that these changes will reduce the number of bugs in my app or bring about other improvements?

I’m delighted to receive a response from someone from Paris (like me), who works with JUCE. Congratulations on your apps!
ToBy

@jdv
Thank you; I’ll give it a try. I’ve used similar tools in the past to address issues within my code, though not on my iOS device. I’ll see if I can pinpoint these threading issues using my iOS device.

bug I don’t know, improvment in timing, that’s for sure.

1 Like

I haven’t used the latest version of the Admob SDK (can’t update for reasons) but for the past 9 years it’s been the only source of crashes in my app (which does not use JUCE).

I’d focus on the crashes that are unrelated to Admob first.

1 Like

Why use a timer in an audio application?
The sampling rate is your timer… you already have something that ticks on the clock, the audio clock.
Don’t count milliseconds, count samples instead.

At a 44100 Hz samplerate, 441 samples are 10 milliseconds… You can compute the proportion between Midi tempo, time signature, tempo division and sampling rate, and establish how many samples you should wait between a note event and the next one. Wherever you have fractional samples, you can round to the nearest integer and bring back the fractional part to the next interval.

Check this code, it’s part of a MidiFile player that I wrote… The Play() function must be called at every sample in the audio loop. See how the timing is calculated in setSampleRate() and setTempo()

#pragma once

#include <JuceHeader.h>

class JuceMidiFilePlayer
{
public:
    JuceMidiFilePlayer()
    {
        PlayMidifile = false;
    }

    ~JuceMidiFilePlayer()
    {
        PlayMidifile = false;
    }

    void setMidiFileObject(const juce::MidiFile& newMidifile)
    {
        // Make sure the playback is stopped before loading a new file
        StartStopMidiFile(false);

        midifile = newMidifile;

        FileLoaded = true;
        tick_count = 0;
        sample_count = 0;
        last_tick = midifile.getLastTimestamp();
        TPQ = midifile.getTimeFormat();
        lastSampleOffset = 0.0;
        trackEventStart.clear();
        trackEventStart.insertMultiple(0, 0, midifile.getNumTracks());

        setTempo(tempoBPM);
        if (onLoad != nullptr)
            onLoad();

        DBG("MidiFile loaded.");
        DBG("midifile.getLastTimestamp() = " << midifile.getLastTimestamp()); // last tick
        DBG("midifile.getTimeFormat() = " << midifile.getTimeFormat()); // tpq
        DBG("midifile.getNumTracks() = " << midifile.getNumTracks());
    }

    void LoadMidiFileFromURL(const URL& url)
    {
        jassert(processMidi != nullptr);
        jassert(allNotesOff != nullptr);

        std::unique_ptr<juce::InputStream> inputStream(juce::URLInputSource(url).createInputStream());

        // Make sure the playback is stopped before loading a new file
        StartStopMidiFile(false);

        juce::MidiFile mf;
        if (mf.readFrom(*inputStream.get()))
            setMidiFileObject(mf);
    }

    void Unload()
    {
        StartStopMidiFile(false);
        if (onEOF != nullptr) onEOF();

        midifile.clear();
        FileLoaded = false;
    }

    void Rewind()
    {
        tick_count = 0;
        trackEventStart.fill(0);
    }

    void StartStopMidiFile(bool b)
    {
        // Call AllNotesOff when Stop button is hit
        if (!b) allNotesOff();

        // Don't play if no file is loaded
        if (!FileLoaded)
        {
            if (onEOF != nullptr) onEOF();
            return;
        }

        // No double play
        if (b && PlayMidifile) return;

        // Rewind
        if (!b && !PlayMidifile)
        {
            sample_count = 0;
            lastSampleOffset = 0.0;
            Rewind();
        }

        // Store status
        PlayMidifile = b;
    }

    void setTempo(double _tempoBPM)
    {
        tempoBPM = _tempoBPM;

        // A single tick duration is 60 / BPM / TPQ
        // Assuming tempo is 120 BPM and TPQ is 480, a tick lasts 1,0416666666666666666666666666667 milliseconds = 1041 microseconds

        tick_duration = 60.0 / tempoBPM / (double)TPQ;
        auto d = (lastSampleOffset + tick_duration * sampleRate);
        tick_duration_in_samples = (int)d;
        lastSampleOffset = d - (double)tick_duration_in_samples;
    }

    double getTempo()
    {
        return 60.0 / (double)TPQ / tick_duration;
    }

    float getPosition01()
    {
        return (float)tick_count / (float)last_tick;
    }

    void setPosition01(float pos)
    {
        allNotesOff();
        tick_count = juce::jmap<float>(pos, 0, last_tick);
        trackEventStart.fill(0);
    }

    void setSampleRate(double sr)
    {
        sampleRate = sr;

        // Set a starting tick_length for a tempo of 120 BPM and 480 TPQ
        //tick_duration = 60000.0 / 120.0 / 480.0;
        //tick_duration_in_samples = tick_duration * sampleRate / 1000.f;
        setTempo(tempoBPM);

        sample_count = 0;
        lastSampleOffset = 0.0;
        Rewind();
    }

    // To be called in the audio loop for each single sample
    void Play()
    {
        if (!PlayMidifile) return;

        // Shift to the next tick after the required tick duration
        if (++sample_count >= tick_duration_in_samples)
        {
            sample_count = 0;

            // Play events from all tracks in the Midi File
            for (int t = 0; t < midifile.getNumTracks(); t++)
            {
                // Get single track
                auto Sequence = midifile.getTrack(t);

                // Search events in track having a timestamp that matches the current tick_count
                for (int e = trackEventStart[t]; e < Sequence->getNumEvents(); ++e)
                {
                    auto msg = Sequence->getEventPointer(e)->message;

                    // Play all events matching this timestamp
                    if (msg.getTimeStamp() == (double)tick_count)
                    {
                        // Update tempo if it's a tempo event
                        if (msg.isTempoMetaEvent())
                        {
                            TPQ = midifile.getTimeFormat();
                            tick_duration = msg.getTempoMetaEventTickLength(TPQ);
                            setTempo(getTempo());
                        }

                        // Send Midi
                        processMidi(msg.getRawData(), msg.getRawDataSize());
                    }

                    // If the timestamp goes past the tick_count, skip scanning the rest of the track
                    if (msg.getTimeStamp() > (double)tick_count)
                    {
                        // Store last event index
                        trackEventStart.set(t, e);

                        break;
                    }
                }
            }

            // Advance to the next tick
            tick_count++;

            // Check end of file and stop
            if (tick_count >= last_tick)
            {
				PlayMidifile = false;
				allNotesOff();
				if (onEOF != nullptr) onEOF();
            }
        }
    }

    //==============================================================================

    // These public variables tell whether the file is loaded and is playing
    MidiFile midifile;
    bool PlayMidifile = false;
    bool FileLoaded = false;
    int TPQ = 480;

    // Set a lambda for receiving Midi Data
    std::function<void(const unsigned char* midiData, int chunkLength)> processMidi;

    // Set a lambda for calling the All-notes-off event on stop
    std::function<void(void)> allNotesOff;

    // Set a lambda that, if defined, is called when a new Midi File is loaded
    std::function<void(void)> onLoad = nullptr;

    // Set a lambda that, if defined, is called when the playback reaches the end of the file
    std::function<void(void)> onEOF = nullptr;


private:
    juce::Array<int> trackEventStart;
    double tick_duration = 1.041; // 120 BPM @ 480 TPQ
    long tick_count = 0, last_tick = 1;
    int sample_count = 0, tick_duration_in_samples = 0;
    double sampleRate = 44100.0, lastSampleOffset = 0.0;
    double tempoBPM = 120.0;
};

1 Like

Thank you so much for pointing that out. It’s my first app, and I’m a beginner, so I didn’t think about it. It makes sense now, and I appreciate your guidance.
Thanks a lot for the code snippet; it’ll be really helpful.
Have a great day!

1 Like

Hello everyone,

I am encountering a persistent bug that affects approximately 1% of my users (this bug appears to occur across a variety of Android devices and is not specific to any particular version of Android), but unfortunately, I am unable to reproduce this bug on my Android devices. Below is the Firebase Crashlytics stack trace:

Crashed: Thread: SIGABRT  0x0000000000000000
#00 pc 0x8cdb4 libc.so (BuildId: 3908c7c57fa04c64df24425cf16523cf)
#01 pc 0x8cd84 libc.so (BuildId: 3908c7c57fa04c64df24425cf16523cf)
#02 pc 0x771b00 libart.so (BuildId: 735f12f804f88d62a2cb437261076ff7)
#03 pc 0x357d0 libbase.so (BuildId: 6f67f69ff36b970d0b831cfdab3b578d)
#04 pc 0x34d58 libbase.so (BuildId: 6f67f69ff36b970d0b831cfdab3b578d)
#05 pc 0x3ec9b0 libart.so (BuildId: 735f12f804f88d62a2cb437261076ff7)
#06 pc 0xa78ac4 libjuce_jni.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...) [jni.h:631]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#07 pc 0xd75350 libjuce_jni.so (juce::sendAccessibilityEventImpl(juce::AccessibilityHandler const&, int, int) [juce_android_Accessibility.cpp:806]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#08 pc 0xcb3fcc libjuce_jni.so (juce::AccessibilityHandler::takeFocus() [juce_android_Accessibility.cpp:861]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#09 pc 0xcb3e04 libjuce_jni.so (juce::AccessibilityHandler::grabFocusInternal(bool) [juce_AccessibilityHandler.cpp:293]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#10 pc 0xcbfccc libjuce_jni.so (juce::Component::takeKeyboardFocus(juce::Component::FocusChangeType) [juce_AccessibilityHandler.cpp:280]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#11 pc 0xcbd1e8 libjuce_jni.so (juce::Component::grabKeyboardFocusInternal(juce::Component::FocusChangeType, bool) [juce_Component.cpp:2886]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#12 pc 0xcbd240 libjuce_jni.so (juce::Component::grabKeyboardFocusInternal(juce::Component::FocusChangeType, bool) [juce_Component.cpp:2897]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#13 pc 0xcb4cb8 libjuce_jni.so (juce::Component::setVisible(bool) [juce_Component.cpp:2914]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#14 pc 0x8c2514 libjuce_jni.so (WindowUser::paint(juce::Graphics&) [Interface.cpp:1426]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#15 pc 0xcbad70 libjuce_jni.so (juce::Component::paintComponentAndChildren(juce::Graphics&) [juce_Component.cpp:1986]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#16 pc 0xcbac64 libjuce_jni.so (juce::Component::paintEntireComponent(juce::Graphics&, bool) [juce_Component.cpp:2083]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#17 pc 0xcbaff8 libjuce_jni.so (juce::Component::paintComponentAndChildren(juce::Graphics&) [juce_Component.cpp:1970]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#18 pc 0xcbac64 libjuce_jni.so (juce::Component::paintEntireComponent(juce::Graphics&, bool) [juce_Component.cpp:2083]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#19 pc 0xe2092c libjuce_jni.so (juce::OpenGLContext::CachedImage::paintComponent() [juce_OpenGLContext.cpp:458]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#20 pc 0xe20168 libjuce_jni.so (juce::OpenGLContext::CachedImage::renderFrame() [juce_OpenGLContext.cpp:293]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#21 pc 0xe1ed5c libjuce_jni.so (juce::OpenGLContext::CachedImage::runJob() [juce_OpenGLContext.cpp:539]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#22 pc 0xb4a134 libjuce_jni.so (juce::ThreadPool::runNextJob(juce::ThreadPool::ThreadPoolThread&) [juce_ThreadPool.cpp:384]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#23 pc 0xb98fa8 libjuce_jni.so (juce::ThreadPool::ThreadPoolThread::run() [juce_ThreadPool.cpp:36]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#24 pc 0xb46a2c libjuce_jni.so (juce::Thread::threadEntryPoint() [juce_Thread.cpp:96]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#25 pc 0xb77670 libjuce_jni.so (juce::threadEntryProc(void*) [juce_Thread.cpp:118]) (BuildId: 5d618f61669ccb79d008856be9a890adeaf03342)
#26 pc 0xfba4c libc.so (BuildId: 3908c7c57fa04c64df24425cf16523cf)
#27 pc 0x8e5f0 libc.so (BuildId: 3908c7c57fa04c64df24425cf16523cf)

This issue seems to occur when my app is redrawing the interface, such as displaying an advertisement, a full-screen message, or upon the completion of the app’s initialization when the interface is being drawn.

In the stack trace, there are references to paintComponent, OpenGLContext (my MainContentComponent is an OpenGLAppComponent for Android), along with grabKeyboardFocusInternal and takeKeyboardFocus. This leads me to suspect that the issue might be related to Android’s keyboard focus handling. However, in my app, there is only one place where I explicitly request keyboard focus, which is for a TextEditor used for entering preset names. But the app doesn’t crash at this point.

As a precaution, I’ve added setWantsKeyboardFocus(false) in various places and only enable it (setWantsKeyboardFocus(true)) when the Android keyboard is needed. As soon as the user finishes typing, I immediately revert it back to setWantsKeyboardFocus(false). However, the bug persists…

Are there other components that might be requesting keyboard focus? I am under the impression that by default, all components (like Sliders, for example) have KeyboardFocus set to false, correct?

If anyone has any suggestions or insights that could help me make progress on this issue, it would be greatly appreciated.

Thanks,
ToBy

Hello,

My issue remains unresolved. This bug is affecting approximately 1% of my users across various Android devices (No similar problem on iOS).

To recap, the bug seems to occur during interface redrawing events, or, more perplexingly, when the initialise() function is unexpectedly called again while the app has been running for a while, a behavior that I am unable to understand or explain.
The stack trace suggests involvement of OpenGL context and keyboard focus handling, but despite various adjustments, including using setWantsKeyboardFocus(false), the issue persists.

I’m still unable to reproduce this bug on my own devices, which makes it particularly difficult to debug. The stack trace is as follows:

Crashed: Thread: SIGABRT 0x0000000000000000 #00 pc 0x950b0 libc.so (BuildId: 02a91a85343debb2911714273ff2b670) #01 pc 0x95080 libc.so (BuildId: 02a91a85343debb2911714273ff2b670) #02 pc 0x79120c libart.so (BuildId: 5252e4c81acd91f2620b389dd7543d90) #03 pc 0x357d0 libbase.so (BuildId: 9facd15119941cde449a7a03cf8ba918) #04 pc 0x34d58 libbase.so (BuildId: 9facd15119941cde449a7a03cf8ba918) #05 pc 0x3f0904 libart.so (BuildId: 5252e4c81acd91f2620b389dd7543d90) #06 pc 0xa86ebc libjuce_jni.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...) [jni.h:631]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #07 pc 0xd83748 libjuce_jni.so (juce::sendAccessibilityEventImpl(juce::AccessibilityHandler const&, int, int) [juce_android_Accessibility.cpp:806]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #08 pc 0xcc23c4 libjuce_jni.so (juce::AccessibilityHandler::takeFocus() [juce_android_Accessibility.cpp:861]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #09 pc 0xcc21fc libjuce_jni.so (juce::AccessibilityHandler::grabFocusInternal(bool) [juce_AccessibilityHandler.cpp:293]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #10 pc 0xcce0c4 libjuce_jni.so (juce::Component::takeKeyboardFocus(juce::Component::FocusChangeType) [juce_AccessibilityHandler.cpp:280]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #11 pc 0xccb5e0 libjuce_jni.so (juce::Component::grabKeyboardFocusInternal(juce::Component::FocusChangeType, bool) [juce_Component.cpp:2886]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #12 pc 0xccb638 libjuce_jni.so (juce::Component::grabKeyboardFocusInternal(juce::Component::FocusChangeType, bool) [juce_Component.cpp:2897]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #13 pc 0xcc30b0 libjuce_jni.so (juce::Component::setVisible(bool) [juce_Component.cpp:2914]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #14 pc 0x8d0028 libjuce_jni.so (WindowUser::paint(juce::Graphics&) [Interface.cpp:1425]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #15 pc 0xcc9168 libjuce_jni.so (juce::Component::paintComponentAndChildren(juce::Graphics&) [juce_Component.cpp:1986]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #16 pc 0xcc905c libjuce_jni.so (juce::Component::paintEntireComponent(juce::Graphics&, bool) [juce_Component.cpp:2083]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #17 pc 0xcc93f0 libjuce_jni.so (juce::Component::paintComponentAndChildren(juce::Graphics&) [juce_Component.cpp:1970]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #18 pc 0xcc905c libjuce_jni.so (juce::Component::paintEntireComponent(juce::Graphics&, bool) [juce_Component.cpp:2083]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #19 pc 0xe2ed24 libjuce_jni.so (juce::OpenGLContext::CachedImage::paintComponent() [juce_OpenGLContext.cpp:458]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #20 pc 0xe2e560 libjuce_jni.so (juce::OpenGLContext::CachedImage::renderFrame() [juce_OpenGLContext.cpp:293]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #21 pc 0xe2d154 libjuce_jni.so (juce::OpenGLContext::CachedImage::runJob() [juce_OpenGLContext.cpp:539]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #22 pc 0xb5852c libjuce_jni.so (juce::ThreadPool::runNextJob(juce::ThreadPool::ThreadPoolThread&) [juce_ThreadPool.cpp:384]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #23 pc 0xba73a0 libjuce_jni.so (juce::ThreadPool::ThreadPoolThread::run() [juce_ThreadPool.cpp:36]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #24 pc 0xb54e24 libjuce_jni.so (juce::Thread::threadEntryPoint() [juce_Thread.cpp:96]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #25 pc 0xb85a68 libjuce_jni.so (juce::threadEntryProc(void*) [juce_Thread.cpp:118]) (BuildId: f47ad24381fb5725f02976f33fd04ffd4b9de84c) #26 pc 0xfd0f4 libc.so (BuildId: 02a91a85343debb2911714273ff2b670) #27 pc 0x96a04 libc.so (BuildId: 02a91a85343debb2911714273ff2b670

Since my last post, I’ve continued to investigate and have attempted several other fixes, but to no avail. I’m reaching out again in hopes that someone might have new insights or suggestions on how to approach this problem. Could this be an issue specific to JUCE’s handling of certain Android functionalities?

Any guidance or advice would be greatly appreciated. Thank you for your time and assistance.

Best regards,
ToBy