From 5b7ffb221631927772a3102540d6772c7cbae89a Mon Sep 17 00:00:00 2001 From: Claudear <262350598+claudear@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:56:09 +0000 Subject: [PATCH 1/2] fix: defer ClientIO init() to first API call to prevent JNI crash on Android release mode The constructor was eagerly calling init(), which triggers getApplicationDocumentsDirectory() before the Flutter engine/JNI bridge is fully initialized. This causes "No JNI instance is available" errors on Android in release mode. The call() method already has lazy init logic, so removing the eager init() call is safe. Fixes HS-823973 Co-Authored-By: Claude Opus 4.6 --- lib/src/client_io.dart | 1 - test/src/client_io_test.dart | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/src/client_io_test.dart diff --git a/lib/src/client_io.dart b/lib/src/client_io.dart index 645efba1..b7b89920 100644 --- a/lib/src/client_io.dart +++ b/lib/src/client_io.dart @@ -68,7 +68,6 @@ class ClientIO extends ClientBase with ClientMixin { _endPoint.startsWith(RegExp("http://|https://")), "endPoint $_endPoint must start with 'http'", ); - init(); } @override diff --git a/test/src/client_io_test.dart b/test/src/client_io_test.dart new file mode 100644 index 00000000..21268a23 --- /dev/null +++ b/test/src/client_io_test.dart @@ -0,0 +1,51 @@ +@TestOn('vm') +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:appwrite/src/client_io.dart'; + +class FakePathProvider extends PathProviderPlatform { + @override + Future getApplicationDocumentsPath() async => '/tmp/test_cookies'; +} + +void main() { + group('ClientIO', () { + setUp(() { + PathProviderPlatform.instance = FakePathProvider(); + }); + + test('constructor should not eagerly call init()', () { + // Creating a ClientIO should NOT trigger init() immediately. + // If init() is called eagerly, it causes "No JNI instance is available" + // errors on Android in release mode because the platform channels + // are not yet ready when the constructor runs. + final client = ClientIO( + endPoint: 'https://cloud.appwrite.io/v1', + selfSigned: false, + ); + + // init() should not have been called yet - initialization should + // be deferred until the first API call + expect(client.initProgress, isFalse, + reason: 'init() should not be called eagerly in the constructor'); + expect(client.initialized, isFalse, + reason: 'Client should not be initialized until first API call'); + }); + + test('init() should be called lazily on first API call', () async { + final client = ClientIO( + endPoint: 'https://cloud.appwrite.io/v1', + selfSigned: false, + ); + + // Before any call, client should not be initialized + expect(client.initialized, isFalse); + + // Trigger initialization by calling init() directly + await client.init(); + + // After init, client should be initialized + expect(client.initialized, isTrue); + }); + }); +} From 088f2744b12cf1c940511bba854586c96d1801d5 Mon Sep 17 00:00:00 2001 From: Claudear <262350598+claudear@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:59:46 +0000 Subject: [PATCH 2/2] fix: use system temp directory in client_io_test to avoid path errors Co-Authored-By: Claude Opus 4.6 --- test/src/client_io_test.dart | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/src/client_io_test.dart b/test/src/client_io_test.dart index 21268a23..b29a999c 100644 --- a/test/src/client_io_test.dart +++ b/test/src/client_io_test.dart @@ -1,17 +1,34 @@ @TestOn('vm') +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:appwrite/src/client_io.dart'; class FakePathProvider extends PathProviderPlatform { + late String _tempPath; + + FakePathProvider() { + _tempPath = Directory.systemTemp.createTempSync('appwrite_test_').path; + } + @override - Future getApplicationDocumentsPath() async => '/tmp/test_cookies'; + Future getApplicationDocumentsPath() async => _tempPath; } void main() { group('ClientIO', () { + late FakePathProvider fakePathProvider; + setUp(() { - PathProviderPlatform.instance = FakePathProvider(); + fakePathProvider = FakePathProvider(); + PathProviderPlatform.instance = fakePathProvider; + }); + + tearDown(() { + try { + Directory(fakePathProvider._tempPath).deleteSync(recursive: true); + } catch (_) {} }); test('constructor should not eagerly call init()', () {