我需要从 RTDB 获取大量数据(10MB)。所有数据存储到
stations
节点。当我在单个请求中获取所有数据时,有时我的 Android 应用程序会因 OutOfMemory 错误而崩溃。所以我需要使用分页阅读,但我有一个问题。
在我的例子中,从 stations
读取完整数据的请求大约需要 10-12 秒。但是,当我发出多个请求(分页)读取 2MB 的页面时,每个请求也需要大约 10 秒。结果,获取
stations
数据的总时间从 10 秒(单次读取)增加到 50 秒(分页读取)。我可以让分页工作更快吗?谢谢。
private fun fetchDayStationsPaged(dayTag: String, lastNodeId: String? = null, stations: MutableList<StationCloud> = mutableListOf(), callback: (data: List<StationCloud>, errorMessage: LoadError?) -> Unit){
val path = String.format(TimelineManager.KEY_TIMELINE_STATIONS, dayTag)
val query = if (lastNodeId == null)
database
.getReference(path)
.orderByKey()
.limitToFirst(2000)
else
database
.getReference(path)
.orderByKey()
.startAfter(lastNodeId)
.limitToFirst(2000)
Timber.d("loader recursion stations $dayTag/${stations.size}")
fetchDayStations(query) { data, errorMessage ->
if (errorMessage == LoadError.NotExist)
callback(stations, null)
else if (errorMessage != null)
callback(emptyList(), errorMessage)
else {
stations.addAll(data)
fetchDayStationsPaged(dayTag, data.last().nodeId, stations, callback)
}
}
}
private var loaderDisposable: Disposable? = null
private fun fetchDayStations(ref: Query, callback: (data: List<StationCloud>, errorMessage: LoadError?) -> Unit){
loaderDisposable?.dispose()
loaderDisposable = FirebaseHelper
.dbReadAsSingle(ref)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose {
callback(emptyList(), LoadError.Cancelled)
}
.subscribe ({ snapshot ->
if (snapshot.exists()) {
val stations = mutableListOf<StationCloud>()
snapshot.children.forEach { item ->
item.getValue(StationCloud::class.java)?.also { station ->
stations.add(station.copy(nodeId = item.key))
}
}
Timber.d("loader fetchDayStations stations size = ${stations.size}")
callback(stations.toList(), null)
} else
callback(emptyList(), LoadError.NotExist)
}, {
Timber.e(it)
callback(emptyList(), LoadError.CantGet)
})
}
private fun dbReadAsSingle(ref: Query): Single<DataSnapshot> {
return Single.create { emitter ->
ref.get().addOnCompleteListener { task ->
Timber.d("runQueryCloudFirst task succeed = ${task.isSuccessful}")
if (task.isSuccessful && emitter.isDisposed.not()){
Timber.d("runQueryCloudFirst children size = ${task.result.childrenCount}")
emitter.onSuccess(task.result)
} else
task.exception?.also {
//todo check a bug: timeout exception doesn't work when offline
//https://github.com/firebase/firebase-android-sdk/issues/5771
Timber.e(it, "runQueryCloudFirst")
emitter.onError(it)
}
}
}
}
stations
数据,然后返回该数据的一部分。因此,它必须加载每个切片的完整数据,从而导致线性性能问题。Firebase 实时数据库 API 中没有任何内容可以对此进行更改。最好的选择通常是更改数据模型以适应用例。
处理 NoSQL 数据库时的一个好习惯是根据屏幕上显示的内容对数据进行建模,并仅加载实际向用户显示的数据。
station_list_info
可能只包含您实际显示的少数属性。该节点可能会小得多,因此会导致更少的内存问题。