Skip to content

Download callbacks not firing in production for some users #1000

@jillellamudisurya

Description

@jillellamudisurya

We're experiencing a critical issue where download callbacks are not being triggered on certain Android devices, specifically affecting multiple device brands including Redmi, Vivo, Oppo, and OnePlus etc... This appears to be a broader compatibility issue across these manufacturers rather than being specific to particular models.

Issue symptoms:

  • ✅ Downloads enqueue successfully via FlutterDownloader.enqueue()
  • ✅ Download tasks appear in database with correct status when queried
  • ❌ Registered callback function (downloadCallback) is never invoked
  • ❌ Progress updates and completion notifications are completely lost
  • ❌ Users see downloads stuck at 0% progress indefinitely

Environment

  • flutter_downloader version: ^1.11.8

  • Calling this in the main.dart

Initialization

// Called during app startup in main.dart
@pragma('vm:entry-point')
Future<void> initializeBgDownloader() async {
  await FlutterDownloader.initialize(debug: kDebugMode);
}
  • This BgDownloaderHost is wrapper at the top level of App.

Callback Registration & Isolate Setup

class _BgDownloaderHostState extends State<BgDownloaderHost> {
  @override
  void initState() {
    super.initState();
    bgDownloader = GetIt.I.get<BgDownloader>();

    // First bind the background isolate
    bgDownloader._startListening();

    // Then register callback after isolate binding - recommended timing
    FlutterDownloader.registerCallback(downloadCallback);
  }
}

void _startListening({int attempt = 1}) {
  // Prevent infinite retry loops - give up after 5 attempts
  if (attempt > 5) {
    logNonFatal("Port registration failed after 5 attempts");
    return;
  }

  // Remove existing port mapping before registering
  IsolateNameServer.removePortNameMapping('downloader_send_port');

  // Register new port
  final registrationSuccess = IsolateNameServer.registerPortWithName(
      port.sendPort, 'downloader_send_port');

  if (!registrationSuccess) {
    _stopListening();
    _startListening(attempt: attempt + 1); // Retry logic
    return;
  }

  port.listen((dynamic data) async {
    String id = data[0];
    DownloadTaskStatus status = DownloadTaskStatus.fromInt(data[1]);
    int progress = data[2];
    _invokeListeners(id, status, progress);
  });
}

 bool isCallbackRegistered() {
    return PluginUtilities.getCallbackHandle(downloadCallback) != null;
  }

Download Callback Function

@pragma('vm:entry-point')
Future<void> downloadCallback(String id, int status, int progress) async {
  try {
    final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');

    if (send == null) {
      developer.log("SendPort lookup failed - progress update lost for task $id");
      return;
    }

    send.send([id, status, progress]);
  } catch (e, st) {
    developer.log("$e", name: "DownloadCallback", level: 1200, stackTrace: st);
  }
}

Additional Code

Future<Directory> _saveDirectory() async {
    Directory directory;
    if (Platform.isAndroid) {
      final path = await AndroidDownloadPath.downloadPath();
      directory = Directory(path);
    } else {
      directory = await getApplicationDocumentsDirectory();
    }
    return directory;
  }

final taskId = await FlutterDownloader.enqueue(
        url: url,
        savedDir: savedDir.absolute.path,
        showNotification: false,
        openFileFromNotification: true,
        saveInPublicStorage: true,
        fileName: fileName,
        requiresStorageNotLow: false,
      );

🔍 Diagnostic Information

Our comprehensive health checks reveal:

  • isCallbackRegistered() returns true (callback handle exists)
  • ✅ Port registration succeeds (IsolateNameServer.registerPortWithName returns true)
  • ✅ Downloads are enqueued successfully and appear in database

Any insights or potential fixes would be greatly appreciated. We're happy to provide additional debugging information or test potential solutions.


Labels: bug, android, callback, isolate-communication

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions