wails/v3/ANDROID_ARCHITECTURE.md
Lea Anthony 4d8ec29feb feat: Add Android support for Wails v3
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>
2025-11-28 21:06:59 +11:00

1025 lines
32 KiB
Markdown

# 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
1. [Architecture Overview](#architecture-overview)
2. [Core Components](#core-components)
3. [Layer Architecture](#layer-architecture)
4. [File Structure](#file-structure)
5. [Implementation Details](#implementation-details)
6. [Build System](#build-system)
7. [JNI Bridge Details](#jni-bridge-details)
8. [Asset Serving](#asset-serving)
9. [JavaScript Bridge](#javascript-bridge)
10. [Security Considerations](#security-considerations)
11. [Configuration Options](#configuration-options)
12. [Debugging](#debugging)
13. [API Reference](#api-reference)
14. [Troubleshooting](#troubleshooting)
15. [Future Enhancements](#future-enhancements)
## Architecture Overview
### Design Principles
1. **Battery Efficiency First**: All architectural decisions prioritize battery life
2. **No Network Ports**: Asset serving happens in-process via `WebViewAssetLoader`
3. **JNI Bridge Pattern**: Java Activity hosts WebView, Go provides business logic
4. **Wails v3 Compatibility**: Maintain API compatibility with existing Wails v3 applications
5. **Follow Fyne's gomobile pattern**: Use `-buildmode=c-shared` for 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**:
```java
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**:
```java
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**:
```java
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 `WebResourceResponse` with asset data
**Key Method**:
```java
@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**:
```java
@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**:
```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**:
```go
//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**:
```go
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.
```go
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`
```go
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
1. **Go 1.21+** with CGO support
2. **Android SDK** with:
- Platform Tools (adb)
- Build Tools
- Android Emulator
3. **Android NDK r19c+** (r26d recommended)
4. **Java JDK 11+**
### Environment Variables
```bash
# 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
```bash
# 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
```bash
# 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
```bash
cd build/android
./gradlew assembleDebug
# Output: app/build/outputs/apk/debug/app-debug.apk
```
#### 3. Installation
```bash
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
```gradle
// 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:
```go
//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:
```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
```go
// 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
```java
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 `WebViewAssetLoader` requires HTTPS for security
- **Domain**: `wails.localhost` is arbitrary but consistent with Wails conventions
### Path Normalization
```java
// 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:
```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:
```java
webView.addJavascriptInterface(new WailsJSBridge(bridge, webView), "wails");
```
This makes `window.wails` available in JavaScript.
### Security Considerations
1. **@JavascriptInterface annotation** is required for all exposed methods (Android 4.2+)
2. Only specific methods are exposed, not the entire object
3. Input validation should be performed on all received data
### Async Pattern
For non-blocking calls:
```javascript
// 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
```java
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
1. **No arbitrary code execution**: JNI methods have fixed signatures
2. **Input validation**: All strings from Java are validated in Go
3. **Memory safety**: Go's memory management prevents buffer overflows
### Asset Security
1. **Same-origin policy**: Assets only served from `wails.localhost`
2. **No external network**: All assets embedded, no remote fetching
3. **Content Security Policy**: Can be set via HTML headers
## Configuration Options
### AndroidOptions Struct
```go
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
```go
app := application.New(application.Options{
Name: "My App",
Android: application.AndroidOptions{
DisableOverscroll: true,
BackgroundColour: application.NewRGB(27, 38, 54),
},
})
```
### AndroidManifest.xml Configuration
```xml
<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
```bash
# All Wails logs
adb logcat -v time | grep -E "(Wails|WailsBridge|WailsActivity)"
# Using task
task android:logs
```
### WebView Debugging
Enable in debug builds:
```java
if (BuildConfig.DEBUG) {
WebView.setWebContentsDebuggingEnabled(true);
}
```
Then in Chrome: `chrome://inspect/#devices`
### Go Debugging
```go
func androidLogf(level string, format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
println(fmt.Sprintf("[Android/%s] %s", level, msg))
}
```
### Common Issues
1. **"UnsatisfiedLinkError"**: Library not found or wrong architecture
2. **"No implementation found"**: JNI function name mismatch
3. **Blank WebView**: Asset serving not working, check logcat
## API Reference
### Go API (Same as Desktop)
```go
// 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
```javascript
// 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
```javascript
// 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"**
```bash
# 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 `//export` comment is directly above function
**"cannot find package"**
```bash
cd examples/android && go mod tidy
```
### Runtime Errors
**App crashes on startup**
1. Check logcat for stack trace
2. Verify library is in correct jniLibs directory
3. Check architecture matches device/emulator
**WebView shows blank**
1. Enable WebView debugging
2. Check Chrome DevTools for errors
3. Verify `https://wails.localhost/` resolves
**JavaScript bridge not working**
1. Check `wails` object exists: `console.log(window.wails)`
2. Verify `@JavascriptInterface` annotations present
3. 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:
1. **Compatibility**: Same Go code runs on all platforms
2. **Performance**: No network overhead, native rendering
3. **Security**: Sandboxed WebView, validated inputs
4. **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*