Merge pull request #10670 from Stypox/feed-oom

Fix OutOfMemory when fetching feed
This commit is contained in:
Stypox 2023-12-21 22:29:46 +01:00 committed by GitHub
commit 482531836f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 26 deletions

View File

@ -58,7 +58,7 @@ class NotificationHelper(val context: Context) {
.setAutoCancel(true) .setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_SOCIAL) .setCategory(NotificationCompat.CATEGORY_SOCIAL)
.setGroupSummary(true) .setGroupSummary(true)
.setGroup(data.originalInfo.url) .setGroup(data.url)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
// Build a summary notification for Android versions < 7.0 // Build a summary notification for Android versions < 7.0
@ -73,7 +73,7 @@ class NotificationHelper(val context: Context) {
context, context,
data.pseudoId, data.pseudoId,
NavigationHelper NavigationHelper
.getChannelIntent(context, data.originalInfo.serviceId, data.originalInfo.url) .getChannelIntent(context, data.serviceId, data.url)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
0, 0,
false false
@ -88,7 +88,7 @@ class NotificationHelper(val context: Context) {
// Show individual stream notifications, set channel icon only if there is actually // Show individual stream notifications, set channel icon only if there is actually
// one // one
showStreamNotifications(newStreams, data.originalInfo.serviceId, bitmap) showStreamNotifications(newStreams, data.serviceId, bitmap)
// Show summary notification // Show summary notification
manager.notify(data.pseudoId, summaryBuilder.build()) manager.notify(data.pseudoId, summaryBuilder.build())
@ -97,7 +97,7 @@ class NotificationHelper(val context: Context) {
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) { override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
// Show individual stream notifications // Show individual stream notifications
showStreamNotifications(newStreams, data.originalInfo.serviceId, null) showStreamNotifications(newStreams, data.serviceId, null)
// Show summary notification // Show summary notification
manager.notify(data.pseudoId, summaryBuilder.build()) manager.notify(data.pseudoId, summaryBuilder.build())
iconLoadingTargets.remove(this) // allow it to be garbage-collected iconLoadingTargets.remove(this) // allow it to be garbage-collected

View File

@ -277,14 +277,14 @@ class FeedLoadManager(private val context: Context) {
notification.value!!.newStreams = filterNewStreams(info.streams) notification.value!!.newStreams = filterNewStreams(info.streams)
feedDatabaseManager.upsertAll(info.uid, info.streams) feedDatabaseManager.upsertAll(info.uid, info.streams)
subscriptionManager.updateFromInfo(info.uid, info.originalInfo) subscriptionManager.updateFromInfo(info)
if (info.errors.isNotEmpty()) { if (info.errors.isNotEmpty()) {
feedResultsHolder.addErrors( feedResultsHolder.addErrors(
info.errors.map { info.errors.map {
FeedLoadService.RequestException( FeedLoadService.RequestException(
info.uid, info.uid,
"${info.originalInfo.serviceId}:${info.originalInfo.url}", "${info.serviceId}:${info.url}",
it it
) )
} }

View File

@ -3,29 +3,48 @@ package org.schabi.newpipe.local.feed.service
import org.schabi.newpipe.database.subscription.NotificationMode import org.schabi.newpipe.database.subscription.NotificationMode
import org.schabi.newpipe.database.subscription.SubscriptionEntity import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.extractor.Info import org.schabi.newpipe.extractor.Info
import org.schabi.newpipe.extractor.channel.ChannelInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.image.ImageStrategy
/**
* Instances of this class might stay around in memory for some time while fetching the feed,
* because of [FeedLoadManager.BUFFER_COUNT_BEFORE_INSERT]. Therefore this class should contain
* as little data as possible to avoid out of memory errors. In particular, avoid storing whole
* [ChannelInfo] objects, as they might contain raw JSON info in ready channel tabs link handlers.
*/
data class FeedUpdateInfo( data class FeedUpdateInfo(
val uid: Long, val uid: Long,
@NotificationMode @NotificationMode
val notificationMode: Int, val notificationMode: Int,
val name: String, val name: String,
val avatarUrl: String, val avatarUrl: String,
val originalInfo: Info, val url: String,
val serviceId: Int,
// description and subscriberCount are null if the constructor info is from the fast feed method
val description: String?,
val subscriberCount: Long?,
val streams: List<StreamInfoItem>, val streams: List<StreamInfoItem>,
val errors: List<Throwable>, val errors: List<Throwable>,
) { ) {
constructor( constructor(
subscription: SubscriptionEntity, subscription: SubscriptionEntity,
originalInfo: Info, info: Info,
streams: List<StreamInfoItem>, streams: List<StreamInfoItem>,
errors: List<Throwable>, errors: List<Throwable>,
) : this( ) : this(
uid = subscription.uid, uid = subscription.uid,
notificationMode = subscription.notificationMode, notificationMode = subscription.notificationMode,
name = subscription.name, name = info.name,
avatarUrl = subscription.avatarUrl, avatarUrl = (info as? ChannelInfo)?.avatars?.let {
originalInfo = originalInfo, // if the newly fetched info is not from fast feed, then it contains updated avatars
ImageStrategy.imageListToDbUrl(it)
} ?: subscription.avatarUrl,
url = info.url,
serviceId = info.serviceId,
// there is no description and subscriberCount in the fast feed
description = (info as? ChannelInfo)?.description,
subscriberCount = (info as? ChannelInfo)?.subscriberCount,
streams = streams, streams = streams,
errors = errors, errors = errors,
) )
@ -34,7 +53,7 @@ data class FeedUpdateInfo(
* Integer id, can be used as notification id, etc. * Integer id, can be used as notification id, etc.
*/ */
val pseudoId: Int val pseudoId: Int
get() = originalInfo.url.hashCode() get() = url.hashCode()
lateinit var newStreams: List<StreamInfoItem> lateinit var newStreams: List<StreamInfoItem>
} }

View File

@ -12,12 +12,11 @@ import org.schabi.newpipe.database.stream.model.StreamEntity
import org.schabi.newpipe.database.subscription.NotificationMode import org.schabi.newpipe.database.subscription.NotificationMode
import org.schabi.newpipe.database.subscription.SubscriptionDAO import org.schabi.newpipe.database.subscription.SubscriptionDAO
import org.schabi.newpipe.database.subscription.SubscriptionEntity import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.extractor.Info
import org.schabi.newpipe.extractor.channel.ChannelInfo import org.schabi.newpipe.extractor.channel.ChannelInfo
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo
import org.schabi.newpipe.extractor.feed.FeedInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.local.feed.FeedDatabaseManager import org.schabi.newpipe.local.feed.FeedDatabaseManager
import org.schabi.newpipe.local.feed.service.FeedUpdateInfo
import org.schabi.newpipe.util.ExtractorHelper import org.schabi.newpipe.util.ExtractorHelper
import org.schabi.newpipe.util.image.ImageStrategy import org.schabi.newpipe.util.image.ImageStrategy
@ -97,19 +96,15 @@ class SubscriptionManager(context: Context) {
} }
} }
fun updateFromInfo(subscriptionId: Long, info: Info) { fun updateFromInfo(info: FeedUpdateInfo) {
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId) val subscriptionEntity = subscriptionTable.getSubscription(info.uid)
if (info is FeedInfo) {
subscriptionEntity.name = info.name subscriptionEntity.name = info.name
} else if (info is ChannelInfo) { subscriptionEntity.avatarUrl = info.avatarUrl
subscriptionEntity.setData(
info.name, // these two fields are null if the feed info was fetched using the fast feed method
ImageStrategy.imageListToDbUrl(info.avatars), info.description?.let { subscriptionEntity.description = it }
info.description, info.subscriberCount?.let { subscriptionEntity.subscriberCount = it }
info.subscriberCount
)
}
subscriptionTable.update(subscriptionEntity) subscriptionTable.update(subscriptionEntity)
} }