2

を使用して複数のビデオをダウンロードするために、バックグラウンド転送サービスを使用していNSURLSessionます。アプリがバックグラウンド モードの場合、ダウンロードは正常に機能しており、満足しています。私の問題は、キューからダウンロードされた各ビデオのコールバックが必要なことです。

ダウンロードしたビデオごとに次のメソッドが呼び出されることを期待していました。

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
 completionHandler:(void (^)())completionHandler

バックグラウンド転送後にアプリに送信するメッセージがシステムにない場合は、次の方法を使用します。

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

ただし、すべてのダウンロードが完了すると、両方のメソッドが呼び出されます。ダウンロード用に 3 つのビデオを配置し、アプリをバックグラウンドに置きます。3 つのビデオがすべてダウンロードされた後に、両方のメソッドが呼び出されました。


これらのメソッドで私がやっていることは次のとおりです。

AppDelegate

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier 
 completionHandler:(void (^)())completionHandler
{    
    self.backgroundTransferCompletionHandler = completionHandler;
}

ダウンロードViewController

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    if (appDelegate.backgroundTransferCompletionHandler) 
    {
        void (^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
        appDelegate.backgroundTransferCompletionHandler = nil;
        completionHandler();
    }

    NSLog(@"All tasks are finished");
}

各ビデオのダウンロード時にユーザーにローカル通知を表示することは可能ですか? または、バックグラウンドですべての動画のダウンロードが完了するまで待つ必要がありますか?

答えがNOの場合、私の質問は、これら 2 つの異なるコールバックの目的は何ですか? それらを互いに分離するものは何ですか?

4

2 に答える 2

1

各ビデオのダウンロード時にユーザーにローカル通知を表示することは可能ですか? または、バックグラウンドですべての動画のダウンロードが完了するまで待つ必要がありますか?

アプリはhandleEventsForBackgroundURLSession、1 つずつではなく、そのセッションに関連付けられているすべてのダウンロードが完了すると、バックグラウンドでのみ再起動されます。バックグラウンド セッションの考え方は、バックグラウンドでの実行 (または開始と停止の繰り返し) によるバッテリの消耗を最小限に抑えることですが、むしろバックグラウンド デーモンにそれを実行させ、すべてが完了したときに通知することです。

理論的には、それぞれで個別のバックグラウンド セッションをインスタンス化できるかもしれませんが、これはバックグラウンド セッションの乱用 (アプリのスピンアップとバックグラウンドでの実行に費やされる時間を減らすことを目的としています) のように私には思えます。 Apple がその慣行に眉をひそめたとしても、驚かないでください。また、(複数のNSURLSessionオブジェクトを使用して) 不器用な実装が必要になります。

答えが NO の場合、私の質問は、これら 2 つの異なるコールバックの目的は何ですか? それらを互いに分離するものは何ですか?

個別のコールバックの目的は、アプリが再び実行されると、ダウンロードごとに必要な後処理を実行できるようにすることです (一時的な場所から最終的な場所にファイルを移動するなど)。アプリがバックグラウンド モードで再起動されたときにすべてのコールバックが連続してすばやく呼び出される場合でも、ダウンロードごとに個別のコールバックが必要です。さらに、アプリがすでにフォアグラウンドで実行されている場合は、個々のダウンロードを終了時に処理できます。


余談ですが、LombaX は正しく、handleEventsForBackgroundURLSessionバックグラウンド セッションを開始する必要があります。個人的には、オブジェクトcompletionHandlerのラッパーのプロパティを作成するので、インスタンス化して (デリゲート メソッドを呼び出す準備を整えます)、そこに保存します。完了ハンドラーを保存する論理的な場所です。とにかく とそのデリゲートをインスタンス化する必要があり、保存された完了ハンドラーを取得するためにアプリのデリゲートに戻る必要がなくなります。NSURLSessionhandleEventsForBackgroundURLSessioncompletionHandlerNSURLSessionURLSessionDidFinishEventsForBackgroundURLSession

善悪を問わず、私の典型的な実装は、バックグラウンドの NSURLSession オブジェクトをシングルトンにすることです。したがって、私は次のようなものになります:

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {    
    [BackgroundSession sharedSession].savedCompletionHandler = completionHandler;
}

バックグラウンドの NSURLSession を開始し、completionHandler.

于 2016-05-23T10:22:53.800 に答える
1

ここでの問題はNSURLSessionDelegate、現在のダウンロード セッションに関する情報を提供する を使用していることです。ただし、セッション全体ではなく、単一のタスクに関する情報を知りたいとします。このため、NSURLSessionTaskDelegateまたはNSURLSessionDownloadDelegate

具体的には、 を使用してNSURLSessionDownloadDelegate、次のデリゲート メソッドを実装する必要があります。

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location

ダウンロードが完了したときにアプリがバックグラウンド モードの場合、このメソッドは自動的に呼び出されません。ただし、システムによって への呼び出しapplication:handleEventsForBackgroundURLSession:completionHandler:が行われるため、セッションを再構築してイベントに応答する機会が与えられます (たとえば、要求に応じて通知を送信するなど)。詳細については、次を参照してください

iOS では、バックグラウンド転送が完了したとき、または資格情報が必要なときにアプリが実行されていない場合、アプリはバックグラウンドで自動的に再起動され、アプリの UIApplicationDelegate に application:handleEventsForBackgroundURLSession:completionHandler: メッセージが送信されます。この呼び出しには、アプリを起動する原因となったセッションの識別子が含まれています。アプリは、同じ識別子でバックグラウンド構成オブジェクトを作成し、その構成でセッションを作成する前に、その完了ハンドラーを保存する必要があります。新しく作成されたセッションは、進行中のバックグラウンド アクティビティに自動的に再関連付けされます。

最後に、数年前、NSURLSession のラッパーであるオープン ソース プロジェクトを作成しました。このプロジェクトは iOS 7 用に作成されたため、非推奨のメソッドを使用している可能性がありますが、この回答でカバーされている部分は引き続き有効です。FLDownloader へのリンク

編集 ロブの答えの後、私はいくつかのチェックをしました。サスペンド状態のアプリと強制終了状態のアプリでは動作が異なるようです。

  • application:handleEventsForBackgroundURLSession:completionHandler:アプリが閉じられる(強制終了される)と、システムはすべてのダウンロードが終了したときにのみ呼び出しを開始するようです。XCode を iPhone に接続してみましたが、正しいようです。
  • ただし、このリンクの「バックグラウンド転送に関する考慮事項」では、アプリが「一時停止」状態にある場合、次のように表示されているようです。

アプリが中断されている間にいずれかのタスクが完了した場合、デリゲートの URLSession:downloadTask:didFinishDownloadingToURL: メソッドが呼び出され、タスクとそれに関連付けられた新しくダウンロードされたファイルの URL が呼び出されます。

編集

最後のアサーションである、Apple のドキュメントで確認された場合のイベントは、間違っているようです。iPhone 6S、iOS 9.3.2、XCode、Instruments で個人的にチェックしました。2 つのダウンロードを開始し、アプリを閉じました (一時停止状態、Istruments アクティビティ モニターで確認 - プロセスはまだ有効でしたが、CPU 時間は消費されませんでした) が、URLSession:downloadTask:didFinishDownloadingToURL:メソッドは呼び出されませんでした。ただし、両方のダウンロードが終了したときapplication:handleEventsForBackgroundURLSession:completionHandler:に呼び出されました。

于 2016-05-23T09:14:57.303 に答える