react-native-fs icon indicating copy to clipboard operation
react-native-fs copied to clipboard

Download block UI thread with small file

Open Crash-- opened this issue 9 years ago • 8 comments

I'm using RNFS for downloading file inside an app. Sometimes I can use the app during a download but sometimes not.

I've tested with a small file (~10Mo) and the bug occurs, but with a large file (~1Go) it doesn't.

When I check the log with XCode, I can see, with the small file, that the percentage displayed within the app is not the same that it displayed within Xcode. But with the big file I don't have this problem.

I just tested on Android too, and I get the same weird behavior.

Here is the code I'm using. I tried without progressDivider but same result.

download = (idCourse) => {
    begin = (res) => {
      if (res.statusCode == '404') {
        this.props.cancelDownload(this.props.course.id_course)
        return false
      } else {
        this.jobId = res.jobId
        this.props.setPercentage(this.props.course.id_course, 0, this.jobId)
      }
    }

    progress = (data) => {
      const percentage = ((100 * data.bytesWritten) / data.contentLength) | 0;
      this.props.setPercentage(this.props.course.id_course, percentage, this.jobId)
    }
    const localPath = this.props.course.id_course + '.zip';
    const remoteURL = getCourseZipUrl(this.props.course.id_course)
      const downloadDirectory = Platform.OS === 'ios' ? RNFS.DocumentDirectoryPath + '/downloads/' : RNFS.ExternalDirectoryPath + '/downloads/';

      const realLocalPath = downloadDirectory + '/' + localPath;
      const progressDivider = 1;
      const ret = RNFS.downloadFile({fromUrl: remoteURL, toFile: realLocalPath, begin, progress, false, progressDivider})
      ret.promise.then(res => {
        if (res.statusCode == '404') {
          this.props.cancelDownload(this.props.course.id_course)
        } else{
          this.props.setFinished(this.props.course.id_course)
        }
      }).catch(err => {
        console.log('errorDL', err)
      })
  }

 stopDownload = (idJob) => {
    if (idJob !== -1) {
      RNFS.stopDownload(idJob);
      this.props.cancelDownload(this.props.course.id_course)
    }
  }

Without the bug

download_without_bug

With the bug

bug_dl

Crash-- avatar Oct 24 '16 16:10 Crash--

I'm seeing very similar behavior. 9 times out of 10 the UI locks until the download completes on Android and maybe 50/50 on iOS.

hochbrian avatar Sep 11 '17 22:09 hochbrian

Also seeing something that could be this.

kevboh avatar Oct 19 '17 17:10 kevboh

In fact this is something usual. As the percentage cames from the native side, it uses the RN bridge. This bridge is async. So if a lot of “operations” are passing by this bridge in a very short amount at time we get this lag.

A pretty simple workaround is to rise up progress divider if the file is small. Or getting the percentage every X secondes

Crash-- avatar Oct 19 '17 17:10 Crash--

This is strange. We use downloadFile with large Zip-FIles in our Zizzle-App very unblocking. Download happens in the background, a progressbar is shown at the bottom of the screen during the download and while all that the user can navigate thru the application without any implications.

itinance avatar Oct 19 '17 19:10 itinance

Yeah with large file it’s pretty smooth. This is exactly what I tried to explain previously. Downloading large file implies that percentage goes up slowly. So you have several seconds between each increment. As you have several secondes (or dozen or even minutes) the RN bridge is not filled, not saturated because it doesn’t received 100 events in 5 secondes... this is one of the main RN issue

Crash-- avatar Oct 19 '17 19:10 Crash--

Is there a workaround? I have the same issue with blocked UI. In my case I'm downloading multiple files together (calling downloadFile for each file)

Andrfas avatar Jan 04 '18 09:01 Andrfas

I'm also experiencing this issue with small files when UI refreshes progress every second. With large files or when I increase progressDivider UI is not refreshed every second and I'm able to interact with app, though it's still laggy and some small delay exist. I also noticed that when downloading small file is completed, every touch interaction during downloading is queued and executed right after downloading completes. This issue is not present when debugging with Chrome remote debugger, it might be because all tasks are performed by Chrome instead of Android/iOS.

Edit: I managed to decrease an occurrence of blocking UI by disabling re-rendering unnecessary components, so now I'm updating only Text component. But still, app is very laggy when progress event updates Text every second.

stachu2k avatar Mar 06 '18 14:03 stachu2k

did anyone solve this?

nikunjdhamelia avatar Aug 03 '22 12:08 nikunjdhamelia