fluttercommunity / flutter_downloader

Flutter Downloader - A plugin for creating and managing download tasks.
https://pub.dev/packages/flutter_downloader
BSD 3-Clause "New" or "Revised" License
915 stars 515 forks source link

Downloads sometimes fail with error message: `flutter: not found task corresponding to given task id` #248

Open rivetingpeppermint opened 4 years ago

rivetingpeppermint commented 4 years ago

On both Android and iOS, downloads sometimes fail with this error message: flutter: not found task corresponding to given task id

I found that running flutter clean and rebuilding the app seems to make this disappear for a bit (download successful and file can be opened in relevant applications), but when I build the app again after changing something, the error returns. I've enabled Android cleartext traffic, to no avail.

Here's a minimal reproduction of my code:

import 'dart:isolate';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';

void main(){
  _initializeFlutterDownloader();
  runApp(MyApp());
}

_initializeFlutterDownloader()async{
    WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize();
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Downloader Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Downloader Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ReceivePort _port = ReceivePort();

  @override
  void initState() {
    super.initState();
    _downloadListener();
  }

  static void downloadCallback(
      String id, DownloadTaskStatus status, int progress) {
    final SendPort send =
        IsolateNameServer.lookupPortByName('downloader_send_port');
    send.send([id, status, progress]);
  }

  _downloadListener() {
    IsolateNameServer.registerPortWithName(
        _port.sendPort, 'downloader_send_port');
    _port.listen((dynamic data) {
      String id = data[0];
      DownloadTaskStatus status = data[1];
      if (status.toString() == "DownloadTaskStatus(3)") {
        FlutterDownloader.open(taskId: id);
      }
    });
    FlutterDownloader.registerCallback(downloadCallback);
  }

  void _download() async {
    String _localPath =
        (await findLocalPath()) + Platform.pathSeparator + 'Example_Downloads';

    final savedDir = Directory(_localPath);
    bool hasExisted = await savedDir.exists();
    if (!hasExisted) {
      savedDir.create();
    }
    String _url =
        "https://www.colonialkc.org/wp-content/uploads/2015/07/Placeholder.png";
    final download = await FlutterDownloader.enqueue(
      url: _url,
      savedDir: _localPath,
      showNotification: true,
      openFileFromNotification: true,
    );
  }

  Future<String> findLocalPath() async {
    final directory =
        // (MyGlobals.platform == "android")
        // ?
        await getExternalStorageDirectory();
    // : await getApplicationDocumentsDirectory();
    return directory.path;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _download,
        child: Icon(Icons.file_download),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
rivetingpeppermint commented 4 years ago

Here's my flutter doctor results for good measure:

[✓] Flutter (Channel stable, v1.12.13+hotfix.5, on Mac OS X 10.14.6 18G95, locale en-ID)
    • Flutter version 1.12.13+hotfix.5 at /Users/ictmacbook2/flutter
    • Framework revision 27321ebbad (3 months ago), 2019-12-10 18:15:01 -0800
    • Engine revision 2994f7e1e6
    • Dart version 2.7.0

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    • Android SDK at /Users/ictmacbook2/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-29, build-tools 29.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 10.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.3, Build version 10G8
    • CocoaPods version 1.7.5

[✓] Android Studio (version 3.5)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 43.0.1
    • Dart plugin version 191.8593
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)

[✓] VS Code (version 1.42.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.8.1

[✓] Connected device (1 available)
    • iPad • 1ba96acf871024d9097348342ff6d84fe5013c16 • ios • iOS 12.4.1

• No issues found!
om-ha commented 4 years ago

On iOS culprit error ERROR_INVALID_TASK_ID

  1. https://github.com/fluttercommunity/flutter_downloader/blob/44306c1915a74cddaf0c90ba3c72609f2944a76c/ios/Classes/FlutterDownloaderPlugin.m#L625
  2. https://github.com/fluttercommunity/flutter_downloader/blob/44306c1915a74cddaf0c90ba3c72609f2944a76c/ios/Classes/FlutterDownloaderPlugin.m#L663
  3. https://github.com/fluttercommunity/flutter_downloader/blob/44306c1915a74cddaf0c90ba3c72609f2944a76c/ios/Classes/FlutterDownloaderPlugin.m#L683
  4. https://github.com/fluttercommunity/flutter_downloader/blob/44306c1915a74cddaf0c90ba3c72609f2944a76c/ios/Classes/FlutterDownloaderPlugin.m#L729

On Android culprit error is invalid_task_id

  1. https://github.com/fluttercommunity/flutter_downloader/blob/2af60a845e0fc6fba62d5ae9508c9dcd0b793352/android/src/main/java/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.java#L254
  2. https://github.com/fluttercommunity/flutter_downloader/blob/2af60a845e0fc6fba62d5ae9508c9dcd0b793352/android/src/main/java/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.java#L274
  3. https://github.com/fluttercommunity/flutter_downloader/blob/2af60a845e0fc6fba62d5ae9508c9dcd0b793352/android/src/main/java/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.java#L301
  4. https://github.com/fluttercommunity/flutter_downloader/blob/2af60a845e0fc6fba62d5ae9508c9dcd0b793352/android/src/main/java/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.java#L331
om-ha commented 4 years ago

I think the problem is that either the task is not being enqueued successfully, thus there's no associated download task with TaskID. Or After enqueuing it, it's not being resumed successfully.

om-ha commented 4 years ago

Refer to the stream listener's closure:

      String id = data[0];
      DownloadTaskStatus status = data[1];
      if (status.toString() == "DownloadTaskStatus(3)") {
        FlutterDownloader.open(taskId: id);
      }
  1. Please make sure to check for null-safety of data[0] and data[1] before using them, and see what their values are.

  2. Also change this expression

      if (status.toString() == "DownloadTaskStatus(3)")

    To this:

      if (status == DownloadStatus.COMPLETE)
rivetingpeppermint commented 4 years ago

@om-ha I've tried changing my if to this: if (status.toString() == "DownloadTaskStatus(3)" && progress == 100 &&id!=null). Can't use if (status == DownloadStatus.COMPLETE) because the statuses are DownloadTaskStatus(2), DownloadTaskStatus(3), and so on.

The tasks run smoothly sometimes, and sometimes they're just enqueued and not executed. For example, I got this: Download task is enqueued with id(4ba7a418-32a9-4ece-b538-9e469bd1d784) and nothing else, it just stops there.

rivetingpeppermint commented 4 years ago

It appears that the tasks won't run after I performed hot restart. After each build, everything works fine, but once I hot restart, none of the tasks would run.

om-ha commented 4 years ago

Yeah hot restart could interfere with the download tasks integrity. This needs confirmation though.

LaxmikanthMadhyastha commented 4 years ago

Hi, I'm facing a similar issue only on IOS, I want to open the file as soon as it is downloaded without pressing any button, so I have modified the function in the example project in this repo to something like below

void _bindBackgroundIsolate() {
    bool isSuccess = IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
    if (!isSuccess) {
      _unbindBackgroundIsolate();
      _bindBackgroundIsolate();
      return;
    }
    _port.listen((dynamic data) {
      if (debug) {
        print('UI Isolate Callback: $data');
      }
      String id = data[0];
      DownloadTaskStatus status = data[1];
      int progress = data[2];

      final task = _tasks?.firstWhere((task) => task.taskId == id);
      if (task != null) {
        setState(() {
          task.status = status;
          task.progress = progress;
        });
        if (task.status != null && task.status == DownloadTaskStatus.complete && task.taskId != null) {
          _openDownloadedFile(task).then((success) {
            if (!success) {
              Scaffold.of(context).showSnackBar(SnackBar(content: Text('Cannot open this file')));
            }
          });
        }
      }
    });
  }

for the above code I get error saying not found task corresponding to given task id but however adding a Future.delayed inside the if condition solves the problem.It works fine on android even without adding Future.delayed. below is the modified

void _bindBackgroundIsolate() {
    bool isSuccess = IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
    if (!isSuccess) {
      _unbindBackgroundIsolate();
      _bindBackgroundIsolate();
      return;
    }
    _port.listen((dynamic data) {
      if (debug) {
        print('UI Isolate Callback: $data');
      }
      String id = data[0];
      DownloadTaskStatus status = data[1];
      int progress = data[2];

      final task = _tasks?.firstWhere((task) => task.taskId == id);
      if (task != null) {
        setState(() {
          task.status = status;
          task.progress = progress;
        });
        if (task.status != null && task.status == DownloadTaskStatus.complete && task.taskId != null) {
          Future.delayed(Duration(seconds: 2), () {
            _openDownloadedFile(task).then((success) {
              if (!success) {
                Scaffold.of(context).showSnackBar(SnackBar(content: Text('Cannot open this file')));
              }
            });
          });
        }
      }
    });
  }

am I missing something?

Thank you.

BethshanDeveloper commented 4 years ago

I have this same problem on iOS simulator. Thank you LaxmikanthMadhyastha for the Future.delayed fix - this seems to solve the problem for me. However, it's a bit of a hack, and it would be nice if this could be solved in the package :)

ahmadsufyan455 commented 2 years ago

Any solution so far ?

abdullahalamodi commented 1 year ago

look at this it may help

https://stackoverflow.com/a/74984125/9746484