blob: f0a4dc4e29f3d25fa4554a9423b0696dbc92b891 [file] [edit]
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:jni/jni.dart';
import 'generated_plugin.dart';
/// Retrieves the global Android `ApplicationContext` associated with a
/// Flutter engine.
///
/// The `ApplicationContext` is a long-lived singleton tied to the
/// application's lifecycle. It is safe to store and use from any thread.
JObject get androidApplicationContext => JniFlutterPlugin.applicationContext;
/// Retrieves the current Android `Activity` associated with a Flutter engine.
///
/// The `engineId` can be obtained from `PlatformDispatcher.instance.engineId`
/// in Dart.
///
/// **WARNING: This reference is volatile and must be used with care.**
///
/// The Android `Activity` lifecycle is asynchronous. The `Activity` returned
/// by this function can become `null` or stale (destroyed) at any moment,
/// such as during screen rotation or when the app is backgrounded.
///
/// To prevent native crashes, this function has two strict usage rules:
///
/// 1. **Platform Thread Only**: It must *only* be called from the platform
/// thread.
/// 2. **Synchronous Use Only**: The returned `JObject` must be used
/// immediately and synchronously, with no asynchronous gaps (`await`).
///
/// Do not store the returned `JObject` in a field or local variable that
/// persists across an `await`.
///
/// ---
///
/// ### Correct Usage (Synchronous, "Get-and-Use"):
///
/// ```dart
/// void safeCall() {
/// // This is safe because the `Activity` is retrieved and used
/// // in a single, unbroken, synchronous block.
/// final activity = Jni.androidActivity(engineId);
/// if (activity != null) {
/// someGeneratedApi.doSomething(activity);
/// activity.release();
/// }
/// }
/// ```
///
/// ### **DANGEROUS** Usage (Asynchronous Gap):
///
/// ```dart
/// Future<void> dangerousCall() async {
/// // 1. Get the Activity (e.g., Activity "A")
/// final activity = Jni.androidActivity(engineId);
///
/// // 2. An `await` occurs. The main thread is freed.
/// // While waiting, Android might destroy Activity "A" and create "B".
/// await someOtherFuture();
///
/// // 3. CRASH: The code resumes, but `activity` is now a stale
/// // reference to the destroyed Activity "A".
/// if (activity != null) {
/// someGeneratedApi.doSomething(activity); // This will crash
/// activity.release();
/// }
/// }
/// ```
JObject? androidActivity(int engineId) =>
JniFlutterPlugin.getActivity(engineId);