【问题标题】:Getting Random android.database.sqlite.SQLiteBlobTooBigException获取随机android.database.sqlite.SQLiteBlobTooBigException
【发布时间】:2019-08-30 12:32:09
【问题描述】:

我在我的应用程序中使用 Room 作为单一数据源,因此来自后端的所有内容都保存在我的房间数据库中,然后返回一个 Flowable,每次数据更改时都会触发一个事件。这是我的 PlacesDAO:

    @Dao
abstract class PlacesDao {


    @Query("select * from places where placeId = :id")
    abstract fun getPlace(id: String): Flowable<PlaceVO>

    @Query("select * from places where placeId in (:placesIds) order by distance, placeName ASC")
    abstract fun getPlaces(placesIds: List<String>): Flowable<List<PlaceVO>>


    @Query("select * from places join list_results where Places.placeId = list_results.id order by distance, placeName ASC")
    abstract fun getFilteredPlaces(): Flowable<List<PlaceVO>>



    @Query("select * from places join user_places where Places.placeId = user_places.placeId AND user_places.userId = :userId order by distance, placeName ASC ")
    abstract fun getAllByUser(userId: String) : Flowable<List<PlaceVO>>



    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun realSavePlaces(places:List<PlaceVO>)



    fun savePlaces(places: List<PlaceVO>){
        Timber.w("PAGELIST - Saving places again!!")
        realSavePlaces(places)
    }

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun savePlace(place: PlaceVO)



    @Query("DELETE from places")
    abstract fun deleteAll()

    @Query("select * from places")
    abstract fun getAll(): Single<List<PlaceVO>>




    @Query("select * from places where (experienceId IS NOT NULL) AND (experienceId != '') order by placeName")
    abstract fun getMyPlaces(): Flowable<List<PlaceVO>>

    @Query("update places set distance = :distance and distanceSet = 1 where placeId = :id")
    abstract fun updateDistance(id: String, distance: Float)
}

现在,在我的应用中,有一些操作会触发更改此表中的数据,这会导致我的 UI 接收表中包含的所有项目(大约 3000-5000)。

它并不总是发生(这使得它难以重现),但我会时不时地遇到以下崩溃:

Caused by android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=1223, totalRows=114
       at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(SQLiteConnection.java)
       at android.database.sqlite.SQLiteConnection.executeForCursorWindow + 895(SQLiteConnection.java:895)
       at android.database.sqlite.SQLiteSession.executeForCursorWindow + 836(SQLiteSession.java:836)
       at android.database.sqlite.SQLiteQuery.fillWindow + 62(SQLiteQuery.java:62)
       at android.database.sqlite.SQLiteCursor.fillWindow + 157(SQLiteCursor.java:157)
       at android.database.sqlite.SQLiteCursor.onMove + 128(SQLiteCursor.java:128)
       at android.database.AbstractCursor.moveToPosition + 237(AbstractCursor.java:237)
       at android.database.AbstractCursor.moveToNext + 269(AbstractCursor.java:269)
       at com.myapp.android.model.db.dao.PlacesDao_Impl$6.call + 814(PlacesDao_Impl.java:814)
       at com.myapp.android.model.db.dao.PlacesDao_Impl$6.call + 771(PlacesDao_Impl.java:771)
       at io.reactivex.internal.operators.maybe.MaybeFromCallable.subscribeActual + 46(MaybeFromCallable.java:46)
       at io.reactivex.Maybe.subscribe + 4262(Maybe.java:4262)
       at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe$FlatMapMaybeSubscriber.onNext + 132(FlowableFlatMapMaybe.java:132)
       at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnSubscriber.runAsync + 407(FlowableObserveOn.java:407)
       at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run + 176(FlowableObserveOn.java:176)
       at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run + 260(ExecutorScheduler.java:260)
       at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run + 225(ExecutorScheduler.java:225)
       at java.util.concurrent.ThreadPoolExecutor.runWorker + 1167(ThreadPoolExecutor.java:1167)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run + 641(ThreadPoolExecutor.java:641)
       at java.lang.Thread.run + 764(Thread.java:764)

我只存储文本信息,正如这个类所证明的那样:

@Entity(tableName = "places")
data class PlaceVO(
        @PrimaryKey
        var placeId: String,

        var googleId: String,

        var placeName: String,

        var phoneNumber: String,

        @Embedded
        var primaryCategory: Category?,

        var primaryCategoryTags: List<CategoryTag> = emptyList(),

        var secondaryCategories: List<Category>? = emptyList(),

        var images: List<Image>,

        var website: String,

        var formattedAddress: String? = "",

        var vicinity: String = "",

        var vicinityShort: String = "",

        var city: String? = "",

        var neighbourhood: String?,

        var longitude: Double,

        var latitude: Double,

        var openingHours: List<String>,

        var combinedHighlights: List<HighlightCountWrapper>,
        @Embedded
        var ownExperience: OwnExperience?,

        var otherExperiences: List<Experience>,

        var distance: Float?,

        var distanceSet: Boolean = false,

        var comment: String
) : MarkerPlace {

}

体验课:

@Entity
data class Experience(
        @Json(name="id")
        val experienceId: String,
        @Embedded
        val owner: User,
        val description: String?,
        val highlights: List<Highlight>?,
        val images: List<Image> = emptyList(),
        val createdDate: Date,
        val updatedDate: Date,
        var privacyLevel: AddExperience.Privacy? = null)

一些类型转换器:

@TypeConverter
    fun toHighlightWrapperList(value: String): List<HighlightCountWrapper> {
        val type = Types.newParameterizedType(List::class.java, HighlightCountWrapper::class.java)
        return moshi.adapter<List<HighlightCountWrapper>>(type).fromJson(value) ?: emptyList()
    }

    @TypeConverter
    fun fromHighlightWrapperList(list: List<HighlightCountWrapper>): String {
        val type = Types.newParameterizedType(List::class.java, HighlightCountWrapper::class.java)
        var adapter: JsonAdapter<List<HighlightCountWrapper>> = moshi.adapter<List<HighlightCountWrapper>>(type)
        return adapter.toJson(list)
    }

 @TypeConverter
    fun toExperienceList(value: String): List<Experience> {
        val type = Types.newParameterizedType(List::class.java, Experience::class.java)
        return moshi.adapter<List<Experience>>(type).fromJson(value) ?: emptyList()
    }

    @TypeConverter
    fun fromExperienceList(list: List<Experience>): String {
        val type = Types.newParameterizedType(List::class.java, Experience::class.java)
        var adapter: JsonAdapter<List<Experience>> = moshi.adapter<List<Experience>>(type)
        return adapter.toJson(list)
    }

@TypeConverter
    fun toImageList(value: String): List<Image> {
        val type = Types.newParameterizedType(List::class.java, Image::class.java)
        return moshi.adapter<List<Image>>(type).fromJson(value) ?: emptyList()
    }

    @TypeConverter
    fun fromImageList(list: List<Image>): String {
        val type = Types.newParameterizedType(List::class.java, Image::class.java)
        var adapter: JsonAdapter<List<Image>> = moshi.adapter<List<Image>>(type)
        return adapter.toJson(list)
    }

那么我的行怎么可能对 SQLite 来说太大了?尤其是有时会毫无问题地返回完全相同的数据?

【问题讨论】:

  • "那么我的行怎么可能对 SQLite 来说太大了?" -- 大小限制为 1MB,IIRC。你有很多List 属性,包括两个看起来很吓人的List&lt;Image&gt;。 “尤其是有时会毫无问题地返回完全相同的数据?” -- 这可能取决于查询中返回的其他内容。
  • 将它们建模为单独的实体而不是将它们字符串化为 json 会使问题变得更好吗?
  • 显然对我来说这只发生在onePlus设备上

标签: android android-room


【解决方案1】:

我发现如何使用 length() 和 substr() 只请求 1MB(CursorWindow 的最大值为 2MB),也许它会help。 (在您的情况下,您可以简单地将请求分成 100 行的块,然后关闭游标,然后重复) 看来你是在存储图片,这种情况下最好把图片存储在内部存储中,只在数据库中存储文件路径。

尤其是有时会毫无问题地返回完全相同的数据?

如果您的意思是完全相同的行(例如从 1000 到 2000 的行),但来自不同的设备,则可能是 CursorWindow 的“最大尺寸”不同,在我的情况下,它似乎是2MB。

【讨论】:

  • 你能用示例代码更新一下吗
猜你喜欢
  • 2011-06-04
  • 2011-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-16
  • 2012-03-22
  • 2011-02-11
相关资源
最近更新 更多