我正試圖從 Google Drive 下載整個目錄,并將其存盤在我的 Android 設備上。我這樣做的程序包括首先下載所有的目錄資訊,然后遞回地爬過層次結構(從根目錄開始),邊爬邊創建子目錄。下面的遞回方法可以正確地完成這個任務。
問題是這樣的。我沒有辦法知道這個方法何時完成,因為我不知道任何適當的停止條件。我知道一旦最后一個 coroutine 完成,它就完成了,但我無法找出如何跟蹤它們全部完成的時間。
我已經嘗試過了:
我已經嘗試過了。
為我啟動的每個 Coroutine 增加/減少一個計數器,并在計數器達到零時進行檢查。這并不能可靠地作業。
通過他們的作業來跟蹤所有的 coroutine。這似乎很有希望,但我無法弄清楚如何使其發揮作用。
在遞回呼叫中完全不使用 coroutines。這樣做是可行的,但會使操作的時間增加一倍。
如果有任何幫助,我們將不勝感激。如果我的方法不對,我希望能得到糾正。謝謝!
我的遞回方法:
// directoryBeingBuilt作為根目錄開始。
private suspend fun createDirectoryStructure(directoryBeingBuiltId。String, directoryBeingBuiltUri: Uri) {
//為directoryBeingBuilt的所有子目錄創建子目錄。
directoryMap[directoryBeingBuiltId]?.forEach { childDirectory ->
//為每個childDirectory創建一個目錄,并在directoryMap中設定其Uri。
val currentSubdirectoryUri = getOrCreateDirectory(directoryBeingBuiltUri, childDirectory.directoryName) !
childDirectory.directoryUri = currentSubdirectoryUri
//將我們剛剛建立的子目錄設定為directoryBeingBuilt,并重新遞回運行。
lifecycleScope.launch(Dispatchers.Default) {
createDirectoryStructure(childDirectory.directoryId, currentSubdirectoryUri)
}
}
}
我最初是如何呼叫遞回方法的:
withContext(Dispatchers.Default) {
createDirectoryStructure(googleDriveRootDirectoryId.data, backupDirectoryUri)
// I need to call some other method here only once the recursive method is done.
}
directoryMap是一個Map,其key=父目錄id,value=父目錄內的直接子目錄串列:
private val directoryMap: MutableMap<String, List<DirectoryInfoContainer>> = mutableMapOf()
DirectoryInfoContainer:
data class DirectoryInfoContainer(val directoryId: String, val directoryName: String, var directoryUri: Uri?)
getOrCreateDirectory()方法:
private suspend fun getOrCreateDirectory(parentUri: Uri, name: String)。Uri? = withContext(Dispatchers.IO) {
DocumentsContract.createDocument(
requireActivity().contentResolver, parentUri, DocumentsContract.Document.MIME_TYPE_DIR, name
)
}
uj5u.com熱心網友回復:
我沒有測驗這個。只是一個想法。用coroutineScope包裹你的并行作業,這樣暫停函式就不會在所有內部作業完成之前回傳。
另外,你的 suspend 函式是不恰當的,因為它被標記為 suspend,但卻呼叫了阻塞函式,而沒有用 withContext 來包裝它們。這就迫使你用一個 IO 分配器來包裝對該函式的呼叫,這就違背了將其標記為 suspend 的目的,也破壞了 suspend 函式的慣例。
但我可能是錯的。也許getOrCreateDirectory()是一個正確組成的暫停函式,它在內部委托給一個適當的調度器。但如果是這樣的話,當你啟動呼叫此函式的 Coroutines 時,你不應該指定 IO Dispatcher。一個適當的暫停函式總是可以從任何調度器中安全地被呼叫,因為它應該是真正的暫停而不是阻塞。
在下面的代碼中,我假設getOrCreateDirectory是一個暫停函式,它將其阻塞作業包裹在withContext(Dispatchers.IO)中。
private suspend fun createDirectoryStructure(directoryBeingBuiltId: String, directoryBeingBuiltUri: Uri) {
coroutineScope {
//為directoryBeingBuilt的所有子目錄創建子目錄。
directoryMap[directoryBeingBuiltId]?.forEach { childDirectory ->
//為每個childDirectory創建一個目錄,并在directoryMap中設定其Uri。
val currentSubdirectoryUri = getOrCreateDirectory(directoryBeingBuiltUri, childDirectory.directoryName) !
childDirectory.directoryUri = currentSubdirectoryUri
//將我們剛剛建立的子目錄設定為directoryBeingBuilt并重新遞回運行。
啟動 {
createDirectoryStructure(childDirectory.directoryId, currentSubdirectoryUri)
}
}
}
}
因此,現在如果你在一個suspend函式中呼叫這個函式(不需要用withContext包裹它!),它將暫停作業,直到所有這些目錄被創建或拋出一個IOException。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/330533.html
標籤:
