Service constructor

Service(
  1. String libname
)

Creates a Service by opening the shared library at libname, binding the five mandatory C functions, and registering the notification callback.

libname is the path passed to DynamicLibrary.open — typically just the file name (e.g. "libaudio.so") when the library is bundled next to the executable.

Implementation

Service(this.libname) {
  try {
    lib = DynamicLibrary.open(libname);

    startService = lib
        .lookup<NativeFunction<Void Function()>>('start_service')
        .asFunction<void Function()>();

    stopService = lib
        .lookup<NativeFunction<Void Function()>>('stop_service')
        .asFunction<void Function()>();

    getNextMessage = lib
        .lookup<NativeFunction<Pointer<BackendMsg> Function()>>(
          'get_next_message',
        )
        .asFunction<Pointer<BackendMsg> Function()>();

    freeMessage = lib
        .lookup<NativeFunction<Void Function(Pointer<BackendMsg>)>>(
          'free_message',
        )
        .asFunction<void Function(Pointer<BackendMsg>)>();

    _setMessageCallback = lib
        .lookup<
                NativeFunction<
                    Void Function(Pointer<NativeFunction<_NotifyNative>>)>>(
            'set_message_callback')
        .asFunction<void Function(Pointer<NativeFunction<_NotifyNative>>)>();

    // NativeCallable.listener is safe to call from any thread: the C++ worker
    // posts the notification and Dart schedules _onNotify on the event loop.
    _callable = NativeCallable<_NotifyNative>.listener(_onNotify);
    // Register a GC finalizer so that _callable is closed even if a subclass
    // constructor throws after super() — in that case dispose() is never
    // called and the NativeCallable would otherwise keep the isolate alive
    // forever.  dispose() calls _finalizer.detach(this) to prevent the
    // finalizer from firing again after an explicit close.
    _finalizer.attach(this, _callable!, detach: this);
    _setMessageCallback(_callable!.nativeFunction);
  } catch (_) {
    // Construction failed (missing symbol, library not found, …).
    // Mark as disposed so that dispose() becomes a no-op if called via
    // try/finally. Detach the finalizer (no-op if never attached) and close
    // _callable if it was created before the exception.
    _disposed = true;
    _messageController.close();
    _finalizer.detach(this);
    _callable?.close();
    rethrow;
  }
}