0

次のコードを使用して、15019 個の画像を JPG 形式でダウンロードしています。

for (const item of requestUrl) {
    const optios = {
        fromUrl: item.urlPath,
        toFile: item.path,
    };
    downloadFile(optios).promise;
}

❌ アプリのインターフェースがブロックされている

重要な注意: RN は 1 つのスレッドでのみ実行されることがわかっているため、フォアグラウンドとバックグラウンドで実行するヘッドレス JS を実装しました。動作していますが、同じ動作でインターフェイスがブロックされています。

誰でもこの問題を解決するのを手伝ってもらえますか?


react-native-fs": "2.13.3

反応ネイティブ: 0.59.9

4

2 に答える 2

4

react-native-fs は、そのネイティブ実装ですべての作業を行い、反応ネイティブ ブリッジを介して非同期 API を公開します。したがって、js スレッドは、react-native-js を介してファイルをダウンロードしている間、ほとんどの場合アイドル状態であり、js がフォアグラウンドで実行されているかバックグラウンドで実行されているかは問題ではありません。

実際の問題

実装の実際の問題はdownloadFile、ダウンロードをトリガーして Promise を即座に返すことです。したがって、for ループは 15,000 回の反復すべてをほぼ瞬時に実行し、アプリのネイティブ部分で15,000 回の同時ダウンロードを開始します。これはおそらく、アプリ全体がブロックされるほどデバイスの速度を低下させるのに十分です.

解決

多くのファイルをダウンロードする必要がある場合は、何らかのキューとパイプラインを実装して、同時ダウンロード数を制限する必要があります。(デバイスが 15,000 の同時ダウンロードを問題なく処理できたとしても、ダウンロードは利用可能な帯域幅によって制限されます。最大帯域幅を常に使用するには、最大 3 つの同時ダウンロードでおそらく十分です。)

可能なパイプライン ソリューションは次のようになります。

(次のコードは実行またはテストされていません)


/**
 * 
 * @param {item} The item to download
 * @param {item.urlPath} The url to download the item from
 * @param {item.path} The local path to download the item to
 * @returns the Promise returned by react-native-fs downloadFile
 */
const downloadItem = ({ urlPath, path }) => {
  const options = {
    fromUrl: urlPath,
    toFile: path
  }
  return downloadFile(options);
}

/**
 * 
 * @param {Array} Array of the items to download
 * @param {integer} maximum allowed concurrent downloads
 * @returns {Promise} which resolves when all downloads are done
 */
const downloadPipelined = (items, maxConcurrency = 3) => {
    // Clone the items Array because we will mutate the queue
    const queue = [...items];
    /**
     * Calling this function will
     * - donwload one item after another until the queue is empty
     * - return a promise which resolves if the queue is empty and
     *   the last download started from this workerFn is finished
     */
    const workerFn = async () => {
        while (queue.length > 0){
            await downloadItem(queue.shift()); 
        }
    }
    /**
     * starts maxConcurrency of these workers concurrently
     * and saves the promises returned by them to an array
     */ 
    const workers = Array(maxConcurrency).fill(workerFn());
    /**
     * will resolve when all worker promises have resolved
     * (all downloads are done)
     * reject if one rejects
     */
    return Promise.all(workers);
} 
于 2019-08-22T01:26:26.510 に答える
1

are you using some kind of state management? Redux, context, redux-saga/redux-thunk? You can use react-native-background-downloader to try and download through background.

If you are using redux and redux-saga you can download it using fork so it would be non-blocking.

Also, if you are downloading images and storing it or it's path in props or state, it probably is rerendering for each image downloaded.

于 2019-08-22T00:06:07.363 に答える