This commit adds comprehensive Android support for Wails v3, enabling Go applications to run as native Android apps with WebView-based UI. Key features: - Android-specific application implementation with JNI bridge - WebView integration via WebViewAssetLoader for serving assets - JavaScript runtime injection and execution via JNI callbacks - Binding call support with async result callbacks - Event system support for Android platform - Full example Android app with Gradle build system Technical details: - Uses CGO with Android NDK for cross-compilation - Implements JNI callbacks for Go <-> Java communication - Supports both ARM64 and x86_64 architectures - WebView debugging support via Chrome DevTools Protocol - Handles empty response body case in binding calls to prevent panic Files added: - v3/pkg/application/*_android.go - Android platform implementations - v3/pkg/events/events_android.go - Android event definitions - v3/internal/*/\*_android.go - Android-specific internal packages - v3/examples/android/ - Complete example Android application - v3/ANDROID_ARCHITECTURE.md - Architecture documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
32 KiB
Wails v3 Android Architecture
Executive Summary
This document provides a comprehensive technical architecture for Android support in Wails v3. The implementation enables Go applications to run natively on Android with an Android WebView frontend, maintaining the Wails philosophy of using web technologies for UI while leveraging Go for business logic.
Unlike iOS which uses CGO with Objective-C, Android uses JNI (Java Native Interface) to bridge between Java/Kotlin and Go. The Go code is compiled as a shared library (.so) that is loaded by the Android application at runtime.
Table of Contents
- Architecture Overview
- Core Components
- Layer Architecture
- File Structure
- Implementation Details
- Build System
- JNI Bridge Details
- Asset Serving
- JavaScript Bridge
- Security Considerations
- Configuration Options
- Debugging
- API Reference
- Troubleshooting
- Future Enhancements
Architecture Overview
Design Principles
- Battery Efficiency First: All architectural decisions prioritize battery life
- No Network Ports: Asset serving happens in-process via
WebViewAssetLoader - JNI Bridge Pattern: Java Activity hosts WebView, Go provides business logic
- Wails v3 Compatibility: Maintain API compatibility with existing Wails v3 applications
- Follow Fyne's gomobile pattern: Use
-buildmode=c-sharedfor native library
High-Level Architecture
┌─────────────────────────────────────────────────────────────┐
│ Android Application │
├─────────────────────────────────────────────────────────────┤
│ Java/Android Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MainActivity (Activity) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ Android WebView │ │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │ │
│ │ │ │ Web Application (HTML/JS) │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ WailsBridge WailsPathHandler WailsJSBridge│ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ JNI Bridge Layer │
│ System.loadLibrary("wails") │
├─────────────────────────────────────────────────────────────┤
│ Go Runtime (libwails.so) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Wails Application │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │App Logic │ │Services │ │Asset Server │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Comparison with iOS Architecture
| Aspect | iOS | Android |
|---|---|---|
| Native Language | Objective-C | Java |
| Bridge Technology | CGO (C headers) | JNI |
| Build Mode | -buildmode=c-archive (.a) |
-buildmode=c-shared (.so) |
| Entry Point | main.m calls WailsIOSMain() |
MainActivity loads libwails.so |
| WebView | WKWebView | Android WebView |
| URL Scheme | wails://localhost |
https://wails.localhost |
| Asset Interception | WKURLSchemeHandler |
WebViewAssetLoader + PathHandler |
| JS → Native | WKScriptMessageHandler |
@JavascriptInterface |
| Native → JS | evaluateJavaScript: |
evaluateJavascript() |
| App Lifecycle | UIApplicationDelegate |
Activity lifecycle methods |
Core Components
1. Java Components
MainActivity (MainActivity.java)
Purpose: Android Activity that hosts the WebView and manages app lifecycle.
Location: build/android/app/src/main/java/com/wails/app/MainActivity.java
Key Responsibilities:
- Initialize the native Go library via
WailsBridge - Configure and manage the Android WebView
- Set up asset loading via
WebViewAssetLoader - Handle Android lifecycle events (onCreate, onResume, onPause, onDestroy)
- Execute JavaScript in the WebView when requested by Go
Key Methods:
onCreate(Bundle) // Initialize bridge, setup WebView
setupWebView() // Configure WebView settings and handlers
loadApplication() // Load initial URL (https://wails.localhost/)
executeJavaScript(String) // Run JS code (called from Go via JNI)
onResume() / onPause() // Lifecycle events forwarded to Go
onDestroy() // Cleanup resources
onBackPressed() // Handle back navigation
WailsBridge (WailsBridge.java)
Purpose: Manages the JNI connection between Java and Go.
Location: build/android/app/src/main/java/com/wails/app/WailsBridge.java
Key Responsibilities:
- Load the native library (
System.loadLibrary("wails")) - Declare and call native methods
- Manage callbacks for async operations
- Forward lifecycle events to Go
Native Method Declarations:
private static native void nativeInit(WailsBridge bridge);
private static native void nativeShutdown();
private static native void nativeOnResume();
private static native void nativeOnPause();
private static native byte[] nativeServeAsset(String path, String method, String headers);
private static native String nativeHandleMessage(String message);
private static native String nativeGetAssetMimeType(String path);
Key Methods:
initialize() // Call nativeInit, set up Go runtime
shutdown() // Call nativeShutdown, cleanup
serveAsset(path, method, headers) // Get asset data from Go
handleMessage(message) // Send message to Go, get response
getAssetMimeType(path) // Get MIME type for asset
executeJavaScript(js) // Execute JS (callable from Go)
emitEvent(name, data) // Emit event to frontend
WailsPathHandler (WailsPathHandler.java)
Purpose: Implements WebViewAssetLoader.PathHandler to serve assets from Go.
Location: build/android/app/src/main/java/com/wails/app/WailsPathHandler.java
Key Responsibilities:
- Intercept all requests to
https://wails.localhost/* - Forward requests to Go's asset server via
WailsBridge - Return
WebResourceResponsewith asset data
Key Method:
@Nullable
public WebResourceResponse handle(@NonNull String path) {
// Normalize path (/ -> /index.html)
// Call bridge.serveAsset(path, "GET", "{}")
// Get MIME type via bridge.getAssetMimeType(path)
// Return WebResourceResponse with data
}
WailsJSBridge (WailsJSBridge.java)
Purpose: JavaScript interface exposed to the WebView for Go communication.
Location: build/android/app/src/main/java/com/wails/app/WailsJSBridge.java
Key Responsibilities:
- Expose methods to JavaScript via
@JavascriptInterface - Forward messages from JavaScript to Go
- Support both sync and async message patterns
JavaScript Interface Methods:
@JavascriptInterface
public String invoke(String message) // Sync call to Go
@JavascriptInterface
public void invokeAsync(String callbackId, String message) // Async call
@JavascriptInterface
public void log(String level, String message) // Log to Android logcat
@JavascriptInterface
public String platform() // Returns "android"
@JavascriptInterface
public boolean isDebug() // Returns BuildConfig.DEBUG
Usage from JavaScript:
// Synchronous call
const result = wails.invoke(JSON.stringify({type: 'call', ...}));
// Asynchronous call
wails.invokeAsync('callback-123', JSON.stringify({type: 'call', ...}));
// Logging
wails.log('info', 'Hello from JavaScript');
// Platform detection
if (wails.platform() === 'android') { ... }
2. Go Components
Application Layer (application_android.go)
Purpose: Main Go implementation for Android platform.
Location: v3/pkg/application/application_android.go
Build Tag: //go:build android
Key Responsibilities:
- Export JNI functions for Java to call
- Manage global application state
- Handle lifecycle events from Android
- Serve assets and process messages
JNI Exports:
//export Java_com_wails_app_WailsBridge_nativeInit
func Java_com_wails_app_WailsBridge_nativeInit(env *C.JNIEnv, obj C.jobject, bridge C.jobject)
//export Java_com_wails_app_WailsBridge_nativeShutdown
func Java_com_wails_app_WailsBridge_nativeShutdown(env *C.JNIEnv, obj C.jobject)
//export Java_com_wails_app_WailsBridge_nativeOnResume
func Java_com_wails_app_WailsBridge_nativeOnResume(env *C.JNIEnv, obj C.jobject)
//export Java_com_wails_app_WailsBridge_nativeOnPause
func Java_com_wails_app_WailsBridge_nativeOnPause(env *C.JNIEnv, obj C.jobject)
//export Java_com_wails_app_WailsBridge_nativeServeAsset
func Java_com_wails_app_WailsBridge_nativeServeAsset(env *C.JNIEnv, obj C.jobject, path, method, headers *C.char) *C.char
//export Java_com_wails_app_WailsBridge_nativeHandleMessage
func Java_com_wails_app_WailsBridge_nativeHandleMessage(env *C.JNIEnv, obj C.jobject, message *C.char) *C.char
//export Java_com_wails_app_WailsBridge_nativeGetAssetMimeType
func Java_com_wails_app_WailsBridge_nativeGetAssetMimeType(env *C.JNIEnv, obj C.jobject, path *C.char) *C.char
Platform Functions:
func (a *App) platformRun() // Block forever, Android manages lifecycle
func (a *App) platformQuit() // Signal quit
func (a *App) isDarkMode() bool // Query Android dark mode
WebView Window (webview_window_android.go)
Purpose: Implements webviewWindowImpl interface for Android.
Location: v3/pkg/application/webview_window_android.go
Build Tag: //go:build android
Key Methods: Most methods are no-ops or return defaults since Android has a single fullscreen window.
func (w *androidWebviewWindow) execJS(js string) // Execute JavaScript
func (w *androidWebviewWindow) isFullscreen() bool // Always true
func (w *androidWebviewWindow) size() (int, int) // Device dimensions
func (w *androidWebviewWindow) setBackgroundColour(col RGBA) // Set WebView bg
Asset Server (assetserver_android.go)
Purpose: Configure base URL for Android asset serving.
Location: v3/internal/assetserver/assetserver_android.go
Build Tag: //go:build android
var baseURL = url.URL{
Scheme: "https",
Host: "wails.localhost",
}
Other Platform Files
All these files have the //go:build android tag:
| File | Purpose |
|---|---|
init_android.go |
Initialization (no runtime.LockOSThread) |
clipboard_android.go |
Clipboard operations (stub) |
dialogs_android.go |
File/message dialogs (stub) |
menu_android.go |
Menu handling (no-op) |
menuitem_android.go |
Menu items (no-op) |
screen_android.go |
Screen information |
mainthread_android.go |
Main thread dispatch |
signal_handler_android.go |
Signal handling (no-op) |
single_instance_android.go |
Single instance (via manifest) |
systemtray_android.go |
System tray (no-op) |
keys_android.go |
Keyboard handling (stub) |
events_common_android.go |
Event mapping |
messageprocessor_android.go |
Android-specific runtime methods |
File Structure
v3/
├── ANDROID_ARCHITECTURE.md # This document
├── pkg/
│ ├── application/
│ │ ├── application_android.go # Main Android implementation
│ │ ├── application_options.go # Contains AndroidOptions struct
│ │ ├── webview_window_android.go
│ │ ├── clipboard_android.go
│ │ ├── dialogs_android.go
│ │ ├── events_common_android.go
│ │ ├── init_android.go
│ │ ├── keys_android.go
│ │ ├── mainthread_android.go
│ │ ├── menu_android.go
│ │ ├── menuitem_android.go
│ │ ├── messageprocessor_android.go
│ │ ├── messageprocessor_mobile_stub.go # Stub for non-mobile
│ │ ├── screen_android.go
│ │ ├── signal_handler_android.go
│ │ ├── signal_handler_types_android.go
│ │ ├── single_instance_android.go
│ │ └── systemtray_android.go
│ └── events/
│ └── events_android.go
├── internal/
│ └── assetserver/
│ ├── assetserver_android.go
│ └── webview/
│ └── request_android.go
└── examples/
└── android/
├── main.go # Application entry point
├── greetservice.go # Example service
├── go.mod
├── go.sum
├── Taskfile.yml # Build orchestration
├── .gitignore
├── frontend/ # Web frontend (same as other platforms)
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── ...
└── build/
├── config.yml # Build configuration
├── Taskfile.yml # Common build tasks
├── android/
│ ├── Taskfile.yml # Android-specific tasks
│ ├── build.gradle # Root Gradle build
│ ├── settings.gradle
│ ├── gradle.properties
│ ├── gradlew # Gradle wrapper script
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── scripts/
│ │ └── deps/
│ │ └── install_deps.go # Dependency checker
│ └── app/
│ ├── build.gradle # App Gradle build
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── wails/
│ │ └── app/
│ │ ├── MainActivity.java
│ │ ├── WailsBridge.java
│ │ ├── WailsPathHandler.java
│ │ └── WailsJSBridge.java
│ ├── res/
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ └── mipmap-*/ # App icons
│ ├── assets/ # Frontend assets (copied)
│ └── jniLibs/
│ ├── arm64-v8a/
│ │ └── libwails.so # Generated
│ └── x86_64/
│ └── libwails.so # Generated
├── darwin/ # macOS build files
├── linux/ # Linux build files
└── windows/ # Windows build files
Implementation Details
Application Startup Flow
1. Android OS launches MainActivity
│
2. MainActivity.onCreate()
│
├─> WailsBridge.initialize()
│ │
│ └─> System.loadLibrary("wails")
│ │
│ └─> Go runtime starts
│ │
│ └─> nativeInit() called
│ │
│ └─> globalApp = app (store reference)
│
├─> setupWebView()
│ │
│ ├─> Configure WebSettings
│ ├─> Create WebViewAssetLoader with WailsPathHandler
│ ├─> Set WebViewClient for request interception
│ └─> Add WailsJSBridge via addJavascriptInterface
│
└─> loadApplication()
│
└─> webView.loadUrl("https://wails.localhost/")
│
└─> WailsPathHandler.handle("/")
│
└─> WailsBridge.serveAsset("/index.html", ...)
│
└─> nativeServeAsset() (JNI to Go)
│
└─> Go AssetServer returns HTML
Asset Request Flow
WebView requests: https://wails.localhost/main.js
│
▼
WebViewClient.shouldInterceptRequest()
│
▼
WebViewAssetLoader.shouldInterceptRequest()
│
▼
WailsPathHandler.handle("/main.js")
│
▼
WailsBridge.serveAsset("/main.js", "GET", "{}")
│
▼
JNI call: nativeServeAsset(path, method, headers)
│
▼
Go: serveAssetForAndroid(app, "/main.js")
│
▼
Go: AssetServer reads from embed.FS
│
▼
Return: byte[] data
│
▼
WailsPathHandler creates WebResourceResponse
│
▼
WebView renders content
JavaScript to Go Message Flow
JavaScript: wails.invoke('{"type":"call","method":"Greet","args":["World"]}')
│
▼
WailsJSBridge.invoke(message) [@JavascriptInterface]
│
▼
WailsBridge.handleMessage(message)
│
▼
JNI call: nativeHandleMessage(message)
│
▼
Go: handleMessageForAndroid(app, message)
│
▼
Go: Parse JSON, route to service method
│
▼
Go: Execute GreetService.Greet("World")
│
▼
Return: '{"result":"Hello, World!"}'
│
▼
JavaScript receives result
Go to JavaScript Event Flow
Go: app.Event.Emit("time", "Mon, 01 Jan 2024 12:00:00")
│
▼
Go: Call Java executeJavaScript via JNI callback
│
▼
WailsBridge.emitEvent("time", "\"Mon, 01 Jan 2024 12:00:00\"")
│
▼
JavaScript: window.wails._emit('time', "Mon, 01 Jan 2024 12:00:00")
│
▼
Frontend event listeners notified
Build System
Prerequisites
- Go 1.21+ with CGO support
- Android SDK with:
- Platform Tools (adb)
- Build Tools
- Android Emulator
- Android NDK r19c+ (r26d recommended)
- Java JDK 11+
Environment Variables
# Required
export ANDROID_HOME=$HOME/Library/Android/sdk # macOS
export ANDROID_HOME=$HOME/Android/Sdk # Linux
# Optional (auto-detected if not set)
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.1.10909125
# Path additions
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/emulator
Taskfile Commands
# Check/install dependencies
task android:install:deps
# Build Go shared library (default: arm64 for device)
task android:build
# Build for emulator (x86_64)
task android:build ARCH=x86_64
# Build for all architectures (fat APK)
task android:compile:go:all-archs
# Package into APK
task android:package
# Run on emulator
task android:run
# View logs
task android:logs
# Clean build artifacts
task android:clean
Build Process Details
1. Go Compilation
# Environment for arm64 (device)
export GOOS=android
export GOARCH=arm64
export CGO_ENABLED=1
export CC=$NDK/toolchains/llvm/prebuilt/$HOST/bin/aarch64-linux-android21-clang
# Build command
go build -buildmode=c-shared \
-tags android \
-o build/android/app/src/main/jniLibs/arm64-v8a/libwails.so
2. Gradle Build
cd build/android
./gradlew assembleDebug
# Output: app/build/outputs/apk/debug/app-debug.apk
3. Installation
adb install app-debug.apk
adb shell am start -n com.wails.app/.MainActivity
Architecture Support
| Architecture | GOARCH | JNI Directory | Use Case |
|---|---|---|---|
| arm64-v8a | arm64 | jniLibs/arm64-v8a/ |
Physical devices (most common) |
| x86_64 | amd64 | jniLibs/x86_64/ |
Emulator |
| armeabi-v7a | arm | jniLibs/armeabi-v7a/ |
Older devices (optional) |
| x86 | 386 | jniLibs/x86/ |
Older emulators (optional) |
Minimum SDK Configuration
// build/android/app/build.gradle
android {
defaultConfig {
minSdk 21 // Android 5.0 (Lollipop) - 99%+ coverage
targetSdk 34 // Android 14 - Required for Play Store
}
}
JNI Bridge Details
JNI Function Naming Convention
JNI functions must follow this naming pattern:
Java_<package>_<class>_<method>
Example:
//export Java_com_wails_app_WailsBridge_nativeInit
func Java_com_wails_app_WailsBridge_nativeInit(env *C.JNIEnv, obj C.jobject, bridge C.jobject)
Corresponds to Java:
package com.wails.app;
class WailsBridge {
private static native void nativeInit(WailsBridge bridge);
}
JNI Type Mappings
| Java Type | JNI Type | Go CGO Type |
|---|---|---|
| void | void | - |
| boolean | jboolean | C.jboolean |
| int | jint | C.jint |
| long | jlong | C.jlong |
| String | jstring | *C.char (via conversion) |
| byte[] | jbyteArray | *C.char (via conversion) |
| Object | jobject | C.jobject |
String Conversion
// Java String → Go string
goString := C.GoString((*C.char)(unsafe.Pointer(javaString)))
// Go string → Java String (return)
return C.CString(goString) // Must be freed by Java
Thread Safety
- JNI calls must be made from the thread that owns the JNI environment
- Go goroutines cannot directly call JNI methods
- Use channels or callbacks to communicate between goroutines and JNI thread
Asset Serving
WebViewAssetLoader Configuration
assetLoader = new WebViewAssetLoader.Builder()
.setDomain("wails.localhost") // Custom domain
.addPathHandler("/", new WailsPathHandler(bridge)) // All paths
.build();
URL Scheme
- Base URL:
https://wails.localhost/ - Why HTTPS: Android's
WebViewAssetLoaderrequires HTTPS for security - Domain:
wails.localhostis arbitrary but consistent with Wails conventions
Path Normalization
// In WailsPathHandler.handle()
if (path.isEmpty() || path.equals("/")) {
path = "/index.html";
}
MIME Type Detection
MIME types are determined by Go based on file extension. Fallback mapping in Java:
private String getMimeType(String path) {
if (path.endsWith(".html")) return "text/html";
if (path.endsWith(".js")) return "application/javascript";
if (path.endsWith(".css")) return "text/css";
// ... etc
return "application/octet-stream";
}
JavaScript Bridge
Exposed Interface
The WailsJSBridge is added to the WebView as:
webView.addJavascriptInterface(new WailsJSBridge(bridge, webView), "wails");
This makes window.wails available in JavaScript.
Security Considerations
- @JavascriptInterface annotation is required for all exposed methods (Android 4.2+)
- Only specific methods are exposed, not the entire object
- Input validation should be performed on all received data
Async Pattern
For non-blocking calls:
// JavaScript side
const callbackId = 'cb_' + Date.now();
window.wails._callbacks[callbackId] = (result, error) => {
if (error) reject(error);
else resolve(result);
};
wails.invokeAsync(callbackId, message);
// Java side sends response via:
webView.evaluateJavascript(
"window.wails._callback('" + callbackId + "', " + result + ", null);",
null
);
Security Considerations
WebView Security
WebSettings settings = webView.getSettings();
settings.setAllowFileAccess(false); // No file:// access
settings.setAllowContentAccess(false); // No content:// access
settings.setMixedContentMode(MIXED_CONTENT_NEVER_ALLOW); // HTTPS only
JNI Security
- No arbitrary code execution: JNI methods have fixed signatures
- Input validation: All strings from Java are validated in Go
- Memory safety: Go's memory management prevents buffer overflows
Asset Security
- Same-origin policy: Assets only served from
wails.localhost - No external network: All assets embedded, no remote fetching
- Content Security Policy: Can be set via HTML headers
Configuration Options
AndroidOptions Struct
type AndroidOptions struct {
// DisableScroll disables scrolling in the WebView
DisableScroll bool
// DisableOverscroll disables the overscroll bounce effect
DisableOverscroll bool
// EnableZoom allows pinch-to-zoom in the WebView (default: false)
EnableZoom bool
// UserAgent sets a custom user agent string
UserAgent string
// BackgroundColour sets the background colour of the WebView
BackgroundColour RGBA
// DisableHardwareAcceleration disables hardware acceleration
DisableHardwareAcceleration bool
}
Usage
app := application.New(application.Options{
Name: "My App",
Android: application.AndroidOptions{
DisableOverscroll: true,
BackgroundColour: application.NewRGB(27, 38, 54),
},
})
AndroidManifest.xml Configuration
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:usesCleartextTraffic="true" <!-- For localhost -->
android:hardwareAccelerated="true">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:windowSoftInputMode="adjustResize">
</activity>
</application>
</manifest>
Debugging
Logcat Filtering
# All Wails logs
adb logcat -v time | grep -E "(Wails|WailsBridge|WailsActivity)"
# Using task
task android:logs
WebView Debugging
Enable in debug builds:
if (BuildConfig.DEBUG) {
WebView.setWebContentsDebuggingEnabled(true);
}
Then in Chrome: chrome://inspect/#devices
Go Debugging
func androidLogf(level string, format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
println(fmt.Sprintf("[Android/%s] %s", level, msg))
}
Common Issues
- "UnsatisfiedLinkError": Library not found or wrong architecture
- "No implementation found": JNI function name mismatch
- Blank WebView: Asset serving not working, check logcat
API Reference
Go API (Same as Desktop)
// Create application
app := application.New(application.Options{
Name: "App Name",
Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets),
},
Services: []application.Service{
application.NewService(&MyService{}),
},
Android: application.AndroidOptions{...},
})
// Run (blocks on Android)
app.Run()
// Emit events
app.Event.Emit("eventName", data)
JavaScript API
// Call Go service method
const result = await window.wails.Call.ByName('MyService.Greet', 'World');
// Platform detection
if (window.wails.System.Platform() === 'android') { ... }
// Events
window.wails.Events.On('eventName', (data) => { ... });
Android-Specific Runtime Methods
// Vibrate (haptic feedback)
window.wails.Call.ByName('Android.Haptics.Vibrate', {duration: 100});
// Show toast
window.wails.Call.ByName('Android.Toast.Show', {message: 'Hello!'});
// Get device info
const info = await window.wails.Call.ByName('Android.Device.Info');
Troubleshooting
Build Errors
"NDK not found"
# Set NDK path explicitly
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.1.10909125
"undefined reference to JNI function"
- Check function name matches exactly (case-sensitive)
- Ensure
//exportcomment is directly above function
"cannot find package"
cd examples/android && go mod tidy
Runtime Errors
App crashes on startup
- Check logcat for stack trace
- Verify library is in correct jniLibs directory
- Check architecture matches device/emulator
WebView shows blank
- Enable WebView debugging
- Check Chrome DevTools for errors
- Verify
https://wails.localhost/resolves
JavaScript bridge not working
- Check
wailsobject exists:console.log(window.wails) - Verify
@JavascriptInterfaceannotations present - Check for JavaScript errors in console
Future Enhancements
Phase 1: Core Stability
- Complete JNI callback implementation for Go → Java
- Full asset server integration
- Error handling and recovery
- Unit and integration tests
Phase 2: Feature Parity
- Clipboard support
- File dialogs (via Storage Access Framework)
- Notifications
- Deep linking
Phase 3: Android-Specific Features
- Material Design 3 theming integration
- Edge-to-edge display support
- Predictive back gesture
- Picture-in-Picture mode
- Widgets
Phase 4: Advanced Features
- Background services
- Push notifications (FCM)
- Biometric authentication
- App Shortcuts
- Wear OS companion
Conclusion
This architecture provides a solid foundation for Android support in Wails v3. The design prioritizes:
- Compatibility: Same Go code runs on all platforms
- Performance: No network overhead, native rendering
- Security: Sandboxed WebView, validated inputs
- Maintainability: Clear separation of concerns
The implementation follows Android best practices while maintaining the simplicity that Wails developers expect. The JNI bridge pattern, while more complex than iOS's CGO approach, provides robust interoperability between Java and Go.
Key Implementation Status
| Component | Status | Notes |
|---|---|---|
| Java Activity | ✅ Complete | MainActivity with WebView |
| JNI Bridge | ✅ Complete | WailsBridge with native methods |
| Asset Handler | ✅ Complete | WailsPathHandler |
| JS Bridge | ✅ Complete | WailsJSBridge |
| Go Platform Files | ✅ Complete | All *_android.go files |
| Taskfile | ✅ Complete | Build orchestration |
| Gradle Project | ✅ Complete | App structure |
| JNI Implementation | 🔄 Partial | Needs Go → Java callbacks |
| Asset Server Integration | 🔄 Partial | Needs full wiring |
| Testing | ❌ Pending | Needs emulator testing |
Document Version: 1.0 Last Updated: November 2024 Wails Version: v3-alpha