Skip to content

fix(v3): restore Android build and runtime message handling#5022

Open
leaanthony wants to merge 1 commit intov3-alphafrom
fix/android-build-and-runtime
Open

fix(v3): restore Android build and runtime message handling#5022
leaanthony wants to merge 1 commit intov3-alphafrom
fix/android-build-and-runtime

Conversation

@leaanthony
Copy link
Member

@leaanthony leaanthony commented Feb 27, 2026

Summary

  • Restores events.Android struct and JS event mappings removed from events.go (IDs 1259–1270)
  • Passes flags to runtime.Core() in nativeOnPageFinished to match the updated API used by all other platforms
  • Calls setupCommonEvents() in the CGO build path (non-CGO already did this)
  • Replaces the stub handleMessageForAndroid() with real MessageProcessor routing so JS→Go calls work
  • Handles non-JSON string messages (e.g. wails:runtime:ready) before attempting JSON parse

Test plan

  • go build ./pkg/events/ passes
  • go vet ./pkg/events/ passes
  • Android example builds with wails3 task android:package
  • App launches in emulator without crash
  • Clicking "Greet Me!" calls the Go function and returns a greeting
  • wails:runtime:ready string message doesn't cause JSON parse errors in logs

Fixes #5020

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Android event system for activity lifecycle and WebView navigation events.
  • Improvements

    • Enhanced error handling and message routing for Android platform.
    • Improved runtime call processing with better response handling.

Fixes five regressions in Android support:

1. Restore events.Android struct (IDs 1259-1270) and JS event mappings
   that were removed from events.go, breaking events_common_android.go
2. Pass flags to runtime.Core() in nativeOnPageFinished to match the
   updated API signature used by all other platforms
3. Call setupCommonEvents() in the CGO build's run() method (the
   non-CGO build already did this)
4. Replace the stub handleMessageForAndroid() with real MessageProcessor
   routing so JS-to-Go runtime calls (bound methods, clipboard, etc.)
   actually work
5. Handle non-JSON string messages (e.g. "wails:runtime:ready") that
   the JS bridge sends before attempting JSON parse

Fixes #5020

Co-authored-by: Varun Chawla <varun_6april@hotmail.com>
Copilot AI review requested due to automatic review settings February 27, 2026 10:55
@github-actions
Copy link
Contributor

⚠️ Missing Changelog Update

Hi @leaanthony, please update v3/UNRELEASED_CHANGELOG.md with a description of your changes.

This helps us keep track of changes for the next release.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Walkthrough

This PR restores Android event definitions and implements complete message processor routing for Android JNI callbacks. It adds support for handling both JSON and non-JSON messages, integrates Android-specific event lifecycle management, and properly initializes the MessageProcessor during platform initialization with context-based request handling.

Changes

Cohort / File(s) Summary
Android Message Processing
v3/pkg/application/application_android.go, v3/pkg/application/application_android_nocgo.go
Introduces Global MessageProcessor initialization in platformRun, routes JNI callbacks through MessageProcessor with window ID inference, adds non-JSON message detection to handle plain string messages like "wails:runtime:ready", implements structured RuntimeRequest parsing and context-based processing, and enhances error handling with JSON error response marshalling.
Android Runtime Integration
v3/pkg/application/application_android.go
Extends JavaScript injection to obtain runtime JS via runtime.Core with flags parameter, expands asset serving to handle runtime calls with x-wails-window-id headers, and wires platform events to common events during androidApp.run().
Android Event System
v3/pkg/events/events.go
Adds exported Android variable and androidEvents struct defining 12 Android-specific events (activity lifecycle: ActivityCreated through ActivityDestroyed, system: ApplicationLowMemory, ApplicationConfigChanged, and WebView navigation events), implements newAndroidEvents constructor with numeric event IDs (1259–1270), and extends eventToJS mapping with android-prefixed event keys.

Sequence Diagram

sequenceDiagram
    participant AJNICallback as Android JNI<br/>Callback
    participant Handler as handleMessageForAndroid
    participant MsgProc as MessageProcessor
    participant Runtime as Runtime
    participant Response as JSON Response

    AJNICallback->>Handler: message (string)
    alt Non-JSON Message
        Handler->>Response: return {"success":true}
    else JSON Message
        Handler->>Handler: Parse RuntimeRequest
        Handler->>MsgProc: HandleRuntimeCallWithIDs(ctx, req)
        MsgProc->>Runtime: Execute runtime call
        Runtime->>MsgProc: return result
        MsgProc->>Handler: result/error
        Handler->>Response: Marshal JSON response
    end
    Response->>AJNICallback: return response string
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

v3-alpha

Poem

🐰 Android hops with messages now,
Through MessageProcessor's flow,
JSON and strings both understood,
Events wired—as they should!
Runtime calls answered with care,
No more crashes, everywhere! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is incomplete; it lacks the required checklist items and test configuration from the template, though it does contain relevant summary and fixes an issue. Complete the PR description by filling in the test plan checkboxes, adding test configuration details (wails doctor output), and completing the mandatory checklist items.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: restoring Android build functionality and fixing runtime message handling.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from issue #5020: restoring events.Android, updating runtime.Core() API calls, implementing MessageProcessor routing, and handling non-JSON messages.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fix the Android build and runtime issues identified in #5020; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/android-build-and-runtime

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.16.1)
v3/pkg/application/application_android.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

v3/pkg/events/events.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

v3/pkg/application/application_android_nocgo.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes critical Android build and runtime issues that were preventing the Android platform from functioning. The changes restore missing Android event definitions, update the runtime initialization to match the current API, and implement proper message routing for JavaScript-to-Go calls.

Changes:

  • Restores the events.Android struct and JavaScript event mappings (IDs 1259-1270) that were previously removed
  • Updates runtime.Core() call in nativeOnPageFinished to pass flags parameter, matching all other platforms
  • Adds setupCommonEvents() call in CGO build path to map Android platform events to common events
  • Replaces stub handleMessageForAndroid() with full MessageProcessor implementation for routing runtime calls
  • Handles non-JSON string messages (like "wails:runtime:ready") before attempting JSON parse

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
v3/pkg/events/events.go Adds Android event struct with 12 lifecycle/WebView events and their JavaScript mappings
v3/pkg/application/application_android.go Updates runtime.Core() to pass flags, adds setupCommonEvents() call, implements MessageProcessor routing with global state
v3/pkg/application/application_android_nocgo.go Implements MessageProcessor routing with global state, mirrors CGO implementation for non-CGO builds

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v3/pkg/application/application_android.go (1)

526-532: ⚠️ Potential issue | 🟠 Major

Memory leak: C.CString allocations in error response and success response paths are never freed.

C.CString allocates memory that must be freed with C.free. The strings created at lines 527 and 532 are passed to C.createJString and never freed, causing memory leaks on every message handled.

🐛 Proposed fix to free C strings
 	if app == nil {
 		errorResponse := `{"error":"App not initialized"}`
-		return C.createJString(env, C.CString(errorResponse))
+		cErr := C.CString(errorResponse)
+		defer C.free(unsafe.Pointer(cErr))
+		return C.createJString(env, cErr)
 	}

 	// Parse and handle the message
 	response := handleMessageForAndroid(app, goMessage)
-	return C.createJString(env, C.CString(response))
+	cResp := C.CString(response)
+	defer C.free(unsafe.Pointer(cResp))
+	return C.createJString(env, cResp)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/application_android.go` around lines 526 - 532, The
C.CString allocations for errorResponse and the success response are never freed
causing a leak; update the code around the error path and the call to
handleMessageForAndroid so that each C.CString passed into C.createJString is
freed afterwards (use C.free with the unsafe.Pointer of the C string, e.g., free
the result of C.CString(errorResponse) and the C.CString(response) after calling
C.createJString), and ensure any early returns still free their C strings; refer
to createJString, C.CString, C.free, handleMessageForAndroid, errorResponse and
response to locate where to add the frees.
🧹 Nitpick comments (2)
v3/pkg/application/application_android_nocgo.go (1)

188-192: Non-JSON detection heuristic is reasonable but limited.

The check message[0] != '{' works for the documented use case ("wails:runtime:ready"), but note that it will also silently succeed for any malformed message not starting with {. This is acceptable given the current use case, but consider adding a comment documenting the known non-JSON messages for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/application_android_nocgo.go` around lines 188 - 192, The
non-JSON detection in handleMessageForAndroid currently checks only message[0]
!= '{' and returns early; update the code by adding a clarifying comment above
that if-block documenting the known non-JSON messages (e.g.
"wails:runtime:ready") and noting the heuristic’s limitation (it will treat any
non-{ start as non-JSON), so future maintainers understand the intent; reference
handleMessageForAndroid and the androidLogf call so the comment sits next to
that logic.
v3/pkg/application/application_android.go (1)

640-688: Significant code duplication with the non-CGO build.

The handleMessageForAndroid function is nearly identical between application_android.go and application_android_nocgo.go. Consider extracting the shared logic into a common file (e.g., application_android_common.go without build tags, or with //go:build android) to reduce maintenance burden and ensure consistent behavior.

The same JSON escaping issue noted in the non-CGO review (error messages not properly escaped) applies here as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/application_android.go` around lines 640 - 688, The
function handleMessageForAndroid duplicates logic across builds and also returns
unescaped error strings; extract the shared logic into a single helper (e.g.,
processAndroidRuntimeMessage(ctx, app, message) or
handleMessageForAndroidCommon) placed in a common Android file (without
differing build tags) and have both application_android.go and
application_android_nocgo.go call it; while refactoring, replace all direct
fmt.Sprintf(`{"error":"%s"}`, err.Error())/raw string returns with proper JSON
construction (e.g., build a struct like type runtimeResp struct{ Error string
`json:"error"`; Success *bool `json:"success,omitempty"` } and json.Marshal it
or json.Marshal(map[string]string{"error": err.Error()})) so errors are
correctly escaped, and keep existing behaviors such as filling WebviewWindowID
and logging via androidLogf and using messageProc.HandleRuntimeCallWithIDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@v3/pkg/application/application_android_nocgo.go`:
- Around line 196-198: The JSON error strings are built by interpolating
err.Error() directly, which can produce invalid JSON; update the error returns
in handleMessageForAndroid (and the other similar return sites around the
current diff) to json.Marshal the error string and embed the marshaled bytes
into the response (e.g. produce {"error":<marshaledError>}) so quotes and
control characters are escaped; if json.Marshal fails, fall back to a safe
literal (or an empty string) and still log the marshal error via androidLogf —
replace the current fmt.Sprintf(`{"error":"%s"}`, err.Error()) returns with
building the response using the json.Marshal result and keep the existing
androidLogf call.

---

Outside diff comments:
In `@v3/pkg/application/application_android.go`:
- Around line 526-532: The C.CString allocations for errorResponse and the
success response are never freed causing a leak; update the code around the
error path and the call to handleMessageForAndroid so that each C.CString passed
into C.createJString is freed afterwards (use C.free with the unsafe.Pointer of
the C string, e.g., free the result of C.CString(errorResponse) and the
C.CString(response) after calling C.createJString), and ensure any early returns
still free their C strings; refer to createJString, C.CString, C.free,
handleMessageForAndroid, errorResponse and response to locate where to add the
frees.

---

Nitpick comments:
In `@v3/pkg/application/application_android_nocgo.go`:
- Around line 188-192: The non-JSON detection in handleMessageForAndroid
currently checks only message[0] != '{' and returns early; update the code by
adding a clarifying comment above that if-block documenting the known non-JSON
messages (e.g. "wails:runtime:ready") and noting the heuristic’s limitation (it
will treat any non-{ start as non-JSON), so future maintainers understand the
intent; reference handleMessageForAndroid and the androidLogf call so the
comment sits next to that logic.

In `@v3/pkg/application/application_android.go`:
- Around line 640-688: The function handleMessageForAndroid duplicates logic
across builds and also returns unescaped error strings; extract the shared logic
into a single helper (e.g., processAndroidRuntimeMessage(ctx, app, message) or
handleMessageForAndroidCommon) placed in a common Android file (without
differing build tags) and have both application_android.go and
application_android_nocgo.go call it; while refactoring, replace all direct
fmt.Sprintf(`{"error":"%s"}`, err.Error())/raw string returns with proper JSON
construction (e.g., build a struct like type runtimeResp struct{ Error string
`json:"error"`; Success *bool `json:"success,omitempty"` } and json.Marshal it
or json.Marshal(map[string]string{"error": err.Error()})) so errors are
correctly escaped, and keep existing behaviors such as filling WebviewWindowID
and logging via androidLogf and using messageProc.HandleRuntimeCallWithIDs.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 972bb6f and 75eaaf2.

📒 Files selected for processing (3)
  • v3/pkg/application/application_android.go
  • v3/pkg/application/application_android_nocgo.go
  • v3/pkg/events/events.go

Comment on lines +196 to 198
androidLogf("error", "🤖 [handleMessageForAndroid] Failed to parse: %v", err)
return fmt.Sprintf(`{"error":"%s"}`, err.Error())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Error messages aren't JSON-escaped, risking malformed responses.

If err.Error() contains quotes or control characters, the resulting JSON will be invalid. Consider using json.Marshal for the error string to ensure proper escaping.

🔧 Proposed fix using json.Marshal for error strings
 	if err := json.Unmarshal([]byte(message), &req); err != nil {
 		androidLogf("error", "🤖 [handleMessageForAndroid] Failed to parse: %v", err)
-		return fmt.Sprintf(`{"error":"%s"}`, err.Error())
+		errStr, _ := json.Marshal(err.Error())
+		return fmt.Sprintf(`{"error":%s}`, errStr)
 	}

Apply similar changes to the other error returns at lines 219-221 and 229-231.

Also applies to: 219-221, 229-231

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/application_android_nocgo.go` around lines 196 - 198, The
JSON error strings are built by interpolating err.Error() directly, which can
produce invalid JSON; update the error returns in handleMessageForAndroid (and
the other similar return sites around the current diff) to json.Marshal the
error string and embed the marshaled bytes into the response (e.g. produce
{"error":<marshaledError>}) so quotes and control characters are escaped; if
json.Marshal fails, fall back to a safe literal (or an empty string) and still
log the marshal error via androidLogf — replace the current
fmt.Sprintf(`{"error":"%s"}`, err.Error()) returns with building the response
using the json.Marshal result and keep the existing androidLogf call.

@cloudflare-workers-and-pages
Copy link

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: 75eaaf2
Status: ✅  Deploy successful!
Preview URL: https://733bc91c.wails.pages.dev
Branch Preview URL: https://fix-android-build-and-runtim.wails.pages.dev

View logs

@amlwwalker
Copy link

@leaanthony would you like this testing? I can pull this branch and try it?

@amlwwalker
Copy link

When i pull/clean this branch and then run

``
I get

export CGO_ENABLED=1
export GOOS=android

mkdir -p bin
mkdir -p build/android/app/src/main/jniLibs/$JNI_DIR

go build -buildmode=c-shared -tags android,debug -buildvcs=false -gcflags=all="-l" \
  -o build/android/app/src/main/jniLibs/$JNI_DIR/libwails.so

# github.com/wailsapp/wails/v3/pkg/application
../../pkg/application/messageprocessor.go:199:16: undefined: iosMethodNames
  ERROR   exit status 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants