Help: QUtil.getExternalStoragePath Crash & Input Dispatching ANR on Play Store

Hello Niotron Community :waving_hand:

I need expert help fixing two issues that started appearing in my Play Store production version (v31) of my app, which currently has 14.3K+ downloads. Everything was working fine before, but now I’m getting Crashes and ANRs (App Not Responding) in the Play Console related to file handling and UI freeze.


:warning: Crash Details

Error:

SourceFile - com.google.appinventor.components.runtime.util.QUtil.getExternalStoragePath
java.lang.NullPointerException

Status: In production
Affects: v31
Last seen: 2 hours ago

Description:
This crash appears to be triggered when the app tries to copy or access index.html using TaifunFile. It seems the method getExternalStoragePath is returning null, possibly due to storage permission changes in Android 11+ (Scoped Storage).


:fire_extinguisher: ANR (App Not Responding)

Error:

Native method - J.N.MaRRNbDu
Input dispatching timed out (No focused window)

Status: In production
Affects: v31
Last seen: 3 hours ago

Description:
This ANR seems to occur during app startup — maybe while copying or loading the HTML file — which freezes the UI for too long. Possibly a long task (like file copy) running on the main UI thread.


:camera_with_flash: My Current Blocks

Here’s how I currently manage the file and app initialization:

web1

In short:

  • I check if index.html exists in internal or app-specific storage.

  • If not, I copy it using TaifunFile1.Copy from the assets.

  • I then use a clock to call the render procedure and load it into a web viewer.


:magnifying_glass_tilted_left: What I Need Help With

  1. How to correctly handle file copy/access in Android 11+ without causing QUtil.getExternalStoragePath crashes.

  2. Whether I should change ApplicationSpecificDirectory usage or switch to FileScope = App.

  3. How to prevent the ANR (No focused window) — maybe by using a clock delay or async process instead of direct heavy file operations in Screen1.Initialize.

  4. Any improved example or best-practice blocks for safely loading local HTML files.


:brain: What I’ve Tried

  • Verified storage permissions (READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE).

  • Checked both File and TaifunFile components.

  • Tried adding a delay before file copy (with Clock), but still see occasional ANRs.


:white_check_mark: Goal

I want to completely eliminate both the storage crash and UI freeze so that my users get a stable experience on all devices.

Any guidance, examples, or best practices from experienced Niotron developers would be a huge help! :folded_hands:


**From my searching I found someone used this **
Creating Games in Niotron’s WebView: A Guide

This guide provides step-by-step instructions on how to create a simple “Snake Game” using HTML, CSS, and JavaScript, and then run it within the WebView component of Niotron platform. The process involves creating a basic web game and embedding it into an App Inventor project.

Step 1: Preparing Your Game Files

You will start by creating the game’s files: index.html, script.js, and style.css. Below is a brief overview of each file’s purpose:

  • index.html: This file contains the game’s HTML structure, linking the CSS for styling and JavaScript for functionality. It includes a <canvas> element where the game will be rendered and audio elements for game sounds.
  • script.js: The JavaScript file where the game’s logic is defined. It includes functions for drawing the game elements on the canvas, handling user input, and managing game states like starting, ending, and scoring.
  • style.css: The stylesheet that defines the appearance of the game, including the canvas sizing to ensure it covers the entire viewport.

Step 2: Creating the HTML File (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Snake Game</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>    
    <audio id="bgMusic" loop>
        <source src="snake-hissing-6092.mp3" type="audio/mp3">
        Your browser does not support HTML audio.
    </audio>
    <audio id="gameOverSound">
        <source src="snake-hissing-6092.mp3" type="audio/mp3">
        Your browser does not support HTML audio.
    </audio>
    <canvas id="gameCanvas"></canvas>
    
    <script src="script.js"></script>
    <script>
        function resizeGame() {
            const canvas = document.getElementById('gameCanvas');
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            if (typeof drawGame === 'function') {
                drawGame();
            }
        }
        window.addEventListener('load', resizeGame);
        window.addEventListener('resize', resizeGame);
    </script>
</body>
</html>

Step 3: Writing the JavaScript Logic (script.js)

This file includes the logic for the snake’s movement, game boundaries, collision detection, and game over conditions. Due to space constraints, let’s summarize what should be included:

  • Variables for game state (speed, snake position, apple position, score, etc.).
  • Functions to draw the snake, apple, score, and game boundaries on the canvas.
  • Event listeners for touch and keyboard inputs to control the snake.
  • Logic for collision detection, game over conditions, and restarting the game.

Step 4: Styling the Game (style.css)

/* Standard reset to remove margins and paddings */
html, body {
    margin: 0;
    padding: 0;
    overflow: hidden; /* Prevents page scrolling */
    height: 100%; /* Makes body take up the full screen height */
    width: 100%; /* Makes body take up the full screen width */
}

/* Styling for the canvas to ensure it covers the full viewport */
canvas {
    display: block; /* Removes extra spacing below the canvas */
    background-color: #000;
    position: absolute; /* Allows more controlled positioning */
    top: 0;
    left: 0;
}

body {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #f0f0f0;
}

Step 5: Embedding the Game in App Inventor

  1. Create a new App Inventor project.
  2. Add a WebView component to your app’s interface.
  3. Upload the game files (index.html, script.js, style.css, and any assets like audio files) to the project’s assets.
  4. Set the WebView’s Home URL to the index.html file.You can do this by setting the HomeUrl property of the WebView component to the file path of index.html within the project assets.
  5. Test the game on your device or emulator. Ensure the App Inventor app has internet permissions if needed.

blocks

Conclusion

This guide provides a basic overview of creating a web-based game and embedding it within an App Inventor project using the WebView component. The example focused on a simple “Snake Game,” but the principles apply to any web game designed with HTML, CSS, and JavaScript. Experiment with the game’s design and functionality to create unique experiences for your users.

See all files on GitHub GitHub - iagolirapasssos/SnakeGameWebView: Creating Games in App Inventor’s WebView: A Guide

especially on this block
blocks

but from my end I only use this

blocks

or I can say that I am using this first one

1

and not tried this

2

so the question on those two blocks which is the best to use and why?

Thanks in advance if someone can come up with the real best solution to fix and eliminate the Crash & ANR error I get in my Play Store console

You can’t access arbitrary directories anymore… why are you looking for a file index.html in the root directory of your emulated sdcard? Use shared storage like /Download or /Dokuments and use SAF to access non media files which are not owned by your app

https://community.niotron.com/t/some-basics-on-android-storage-system/4748

Taifun

1 Like

am sorry Taifun but I am happy that you have at least commented though my point was how to eliminate that crash and ANR error from my Playstore because all the files are in my app assets and not in user sdcard or file manager.

Kindly if you get time please help me to know how to fix all those out as shown these

ANR

Issues affecting the most users

SourceFile - com.google.appinventor.components.runtime.util.QUtil.getExternalStoragePath

java.lang.NullPointerException

Crash

31 (English + Arabic, French ( 31v ))

2

2

11.1%

8 hours ago

chromium-TrichromeWebViewGoogle.aab-stable-739012230 - WV.gi.b

Input dispatching timed out

ANR

31 (English + Arabic, French ( 31v ))

1

1

5.6%

7 hours ago

[libhwui.so] android::uirenderer::ThreadBase::waitForWork

Input dispatching timed out (No focused window)

ANR

31 (English + Arabic, French ( 31v ))

1

1

5.6%

15 hours ago

SourceFile - com.google.appinventor.components.runtime.util.QUtil.getExternalStorageDir

Input dispatching timed out (No focused window)

ANR

31 (English + Arabic, French ( 31v ))

1

1

5.6%

5 days ago

SourceFile - com.google.appinventor.components.runtime.util.Synchronizer.waitfor

Input dispatching timed out

ANR

31 (English + Arabic, French ( 31v ))

1

1

5.6%

5 days ago

ANR

Thanks in advance for any advance support to eliminate that error.

No, you check if the file is in the assets or ASD and as the file always is in the assets, it always will return true

You should only check if it is in ASD and in case of not, then copy it to there

Taifun

Also do not use the clock component to give the copy method some time for copying. Use the CopyAsync method and call your render method from the Copied event or if the file already is in the ASD, call the render method directly from Screen.Initialize

Also what is the purpose of global render 0 or 1? As far as I can see you can remove that

Taifun

1 Like

Thanks once again for your response.

The reason I originally used the Clock component and the global variable render = 0 → 1 was to give the file-copying process enough time to complete. My intention was that if the copying didn’t respond immediately or failed, the clock would wait briefly and check again before proceeding.

I only used this approach to prevent the app from freezing during startup. Without the delay, some devices were showing ANRs (App Not Responding) and occasional crashes.

Here is the current blocks
a4

Based on your explanation, this is what I understood:

So I want to confirm if I understood correctly.
And if yes, will this approach fully eliminate the ANR and Crash issues?

The issue I’m trying to solve is to prevent the app from freezing at startup. As you can see below, some devices are experiencing ANR or crashes.

Screenshot 1
a1

Screenshot 2
a2

Screenshot 3
a3

If possible, could you help me refactor my blocks to apply the best-practice method using CopyAsync and the Copied event so I can completely eliminate the ANR and crash issues?

I would really appreciate if you can confirm whether these blocks below (based on your advice) are correct and will solve the ANR & Crash issues:
a5

Otherwise, please guide me on how to modify the blocks so they properly address all the Errors and ANR cases.

I also want to mention that on some devices, the Taifun Copied event doesn’t always return success, which is why I originally used the clock as a fallback method.


I would kindly ask you, please help me by adjusting or rebuilding the blocks in the correct way so that I can fully remove the ANR and crash issues. I would be very grateful for your help.
:folded_hands:

Screenshot_20251117_092508_DuckDuckGo

Taifun

Thank you for the clarification. I just wish you could have shown me which specific block I should use, because from what I’ve tested, everything works normally on older Android versions (below Android 15). The file check returns true as long as the file is stored in the app assets.

But on some devices running Android 14 and above — especially Samsung devices — the app cannot find the file in the assets, and I don’t yet understand why this happens.

If you have some free time, I would truly appreciate it if you could kindly read my response and questions in detail, and help me figure out how we can properly fix both problems: the ANR issue and the file check not returning true.

Thank you so much for your time, patience, and support. It really means a lot.

I showed you exactly what to do. Do not check for that file in the assets, because it always is there. Only check, if it is available in ASD

If file exists in ASD

Then render

Else copy it to ASD and after copying then render

Taifun

So you mean I should only do this right?
file

Yes

And remove one of the slashes here

Screenshot_20251119_074203_DuckDuckGo

to get the correct path

Use Do it to check

Taifun

1 Like