Files
ch34/.planning/codebase/ARCHITECTURE.md
2026-04-21 12:57:40 +08:00

9.9 KiB

Architecture

Analysis Date: 2026-04-16

Pattern Overview

Overall: Flutter Platform Plugin (Federated Plugin Pattern)

This is an Android-only Flutter plugin that wraps the WCH CH34X UART library (CH34XUARTDriver.jar). It bridges Flutter/Dart code to native Android USB serial communication via MethodChannel and EventChannel.

Key Characteristics:

  • Static facade pattern for public API (Ch34Manager)
  • Platform interface pattern with token-based verification (Ch34Platform)
  • MethodChannel for command invocation, EventChannel for streaming callbacks
  • Single platform implementation (Android only, Kotlin/Java)
  • Singleton WCH UART manager on the native side

Layers

Dart Public API Layer

  • Purpose: Static facade providing the consumer-facing API
  • Location: D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart
  • Contains: Static methods delegating to Ch34Platform.instance
  • Depends on: Ch34Platform
  • Used by: Flutter app consuming the plugin

Dart Platform Interface Layer

  • Purpose: Abstract interface defining all plugin methods with token verification
  • Location: D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart
  • Contains: abstract class Ch34Platform extends PlatformInterface with 33 method signatures
  • Depends on: plugin_platform_interface package, ch34_types.dart
  • Used by: MethodChannelCh34 for concrete implementation

Dart MethodChannel Implementation Layer

  • Purpose: Bridges abstract platform interface to native Android via Flutter's platform channels
  • Location: D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart
  • Contains: class MethodChannelCh34 extends Ch34Platform with actual channel communication
  • Channels:
    • MethodChannel('ch34') - synchronous method calls
    • EventChannel('ch34/data') - serial data stream
    • EventChannel('ch34/modem') - modem status stream
    • EventChannel('ch34/usb_state') - USB hotplug events
  • Depends on: dart:async, flutter/services.dart
  • Used by: Ch34Manager (via Ch34Platform.instance)

Dart Types Layer

  • Purpose: Shared data types and enums for the plugin
  • Location: D:\code\new_git_code\flutter\ch34\lib\src\types\ch34_types.dart
  • Contains:
    • Enums: DataBits, StopBits, Parity, GpioDirection, GpioValue, SerialErrorType
    • Data classes: GpioStatus, SerialParameter, ModemStatus, UsbDeviceInfo
    • Exception: Ch34Exception (defined in ch34_method_channel.dart)

Android Native Layer

  • Purpose: Actual WCH UART library interaction, USB device management
  • Location: D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\
  • Contains:
    • Ch34Plugin.java - Main plugin entry point, MethodCallHandler
    • Ch34DataStreamHandler.java - Data EventChannel stream handler with WCH callback bridging
    • Ch34ModemStreamHandler.java - Modem status EventChannel stream handler
    • Ch34UsbStateStreamHandler.java - USB hotplug EventChannel with BroadcastReceiver
    • Ch34TypeConverter.java - Type conversion between Dart and WCH types
  • Depends on: CH34XUARTDriver.jar (bundled in android/libs/)
  • Used by: Dart MethodChannel layer

Backward Compatibility Layer

  • Location: D:\code\new_git_code\flutter\ch34\lib\ch34_platform_interface.dart, D:\code\new_git_code\flutter\ch34\lib\ch34_method_channel.dart
  • Purpose: Re-exports from src/ for consumers using old import paths

Data Flow

Device Discovery Flow

  1. Consumer calls Ch34Manager.enumDevice()
  2. Delegates to Ch34Platform.instance.enumDevice()
  3. MethodChannelCh34.enumDevice() invokes methodChannel.invokeMethod('enumDevice')
  4. Ch34Plugin.enumDevice() calls WCHUARTManager.enumDevice() on native side
  5. Native iterates devices, extracts VID/PID/serialCount/chipType
  6. Returns List<Map> through MethodChannel
  7. Dart side deserializes into List<UsbDeviceInfo>

Data Read Flow (Callback Mode)

  1. Consumer calls Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)
  2. MethodChannelCh34 subscribes to EventChannel('ch34/data')
  3. Invokes methodChannel.invokeMethod('registerDataCallback')
  4. Ch34Plugin registers Ch34DataStreamHandler.getWchCallback() with WCHUARTManager.registerDataCallback()
  5. When data arrives, WCH triggers IDataCallback.onData()
  6. Ch34DataStreamHandler bridges data to EventChannel
  7. Dart receives via StreamSubscription, invokes user callback

Data Write Flow

  1. Consumer calls Ch34Manager.writeData(deviceName, serialNumber, data)
  2. Delegates through platform interface to MethodChannelCh34
  3. methodChannel.invokeMethod('writeData', {data: data, ...})
  4. Ch34Plugin.writeData() calls WCHUARTManager.writeData(device, serial, data, length, timeout)
  5. Returns bytes written count

USB Hotplug Event Flow

  1. Ch34UsbStateStreamHandler registers BroadcastReceiver for ACTION_USB_DEVICE_ATTACHED/DETACHED
  2. Android system sends broadcast on USB events
  3. BroadcastReceiver extracts UsbDevice and calls notifyStateChanged()
  4. EventChannel sends {deviceName, connected} to Dart
  5. MethodChannelCh34 invokes _usbStateCallback

Device Open Flow

  1. Consumer calls Ch34Manager.openDevice(deviceName)
  2. Ch34Plugin.openDevice() attempts WCHUARTManager.openDevice(device)
  3. If NoPermissionException, requests permission then retries after 2s delay
  4. On success, adds to openedDevices map and notifies usbStateStreamHandler

State Management

Native State:

  • WCHUARTManager - Singleton instance, lazy-initialized in ensureManagerInitialized()
  • openedDevices: Map<String, UsbDevice> - tracks currently opened devices
  • Three EventChannel.StreamHandler instances - each manages its own EventSink

Dart State:

  • Ch34Platform._instance - Singleton platform instance
  • MethodChannelCh34 maintains _dataSubscription, _modemSubscription, _usbStateSubscription - StreamSubscriptions for event channels
  • Callback references: _dataCallback, _modemCallback, _usbStateCallback

State Cleanup:

  • Ch34Plugin.close() calls closeAllDevices() to disconnect all and clear map
  • Ch34UsbStateStreamHandler.onCancel() unregisters BroadcastReceiver
  • Ch34Manager.close() triggers platform cleanup and cancels all subscriptions

Key Abstractions

Platform Interface (Ch34Platform)

  • Purpose: Abstract contract that decouples Flutter code from native implementation
  • Example: D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart
  • Pattern: Token-based platform interface verification from plugin_platform_interface package
  • Pattern:
static final Object _token = Object();
static Ch34Platform get instance { ... }
static void registerDefaultInstance(Ch34Platform instance) {
  PlatformInterface.verifyToken(instance, _token);
  _instance = instance;
}

Manager Facade (Ch34Manager)

  • Purpose: Static facade simplifying API usage for consumers
  • Example: D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart
  • Pattern: All static methods, all delegate to Ch34Platform.instance
  • Pattern:
static Future<List<UsbDeviceInfo>> enumDevice() {
  return Ch34Platform.instance.enumDevice();
}

Type Converter (Ch34TypeConverter)

  • Purpose: Bridge between Dart enum indices and WCH native enum values
  • Example: D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java
  • Pattern: Static utility class with conversion methods for GPIO direction/value, serial error types

Entry Points

Dart Library Entry

  • Location: D:\code\new_git_code\flutter\ch34\lib\ch34.dart
  • Triggers: Consumer imports package:ch34/ch34.dart
  • Responsibilities: Exports Ch34Manager, Ch34Platform, all types, and Ch34Exception

Plugin Initialization (Android)

  • Location: D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java
  • Trigger: Flutter engine attaches plugin via onAttachedToEngine()
  • Responsibilities: Creates MethodChannel, 3 EventChannels, sets MethodCallHandler
  • Declaration: D:\code\new_git_code\flutter\ch34\android\build.gradle + pubspec.yaml declares package: com.example.ch34, pluginClass: Ch34Plugin

MethodChannel Dispatcher

  • Location: D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java line 80-187
  • Trigger: Dart invokes methodChannel.invokeMethod()
  • Responsibilities: Switch dispatch to 33 method handlers, error handling wrapper

Error Handling

Strategy: Native exceptions caught and converted to MethodChannel error responses with error codes; Dart side uses null-coalescing defaults and Ch34Exception for explicit failures.

Patterns:

  • Native: Each method handler has try-catch, returns result.error(ERROR_CODE, message, null)
    • Error codes: ENUM_DEVICE_FAILED, OPEN_DEVICE_FAILED, PERMISSION_DENIED, WRITE_DATA_FAILED, etc.
  • Dart: Default fallbacks on null results (e.g., result ?? false, result ?? Uint8List(0))
  • Exception class: Ch34Exception in D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart for throwing on Dart side
  • Logging: All errors logged via Log.e(TAG, "...") on Android native side

Cross-Cutting Concerns

Logging: Android android.util.Log with per-class TAGs (Ch34Plugin, Ch34DataStream, Ch34ModemStream, Ch34UsbStateStream). Dart side uses flutter/foundation.dart debugPrint() for callback errors.

Validation: Device name resolution via getDeviceOrThrow() which searches openedDevices map first, then falls back to manager.enumDevice() lookup.

Authentication/Permission: USB permission flow handled in openDevice() - attempts open, if NoPermissionException, calls manager.requestPermission() then retries after 2-second delay.


Architecture analysis: 2026-04-16