From 3adf761158bb5796d27fd5b9ee2690583f439926 Mon Sep 17 00:00:00 2001 From: Koen J Date: Fri, 30 Aug 2024 13:44:14 +0200 Subject: [PATCH] Fixed resource leak. --- .../platformplayer/activities/MainActivity.kt | 69 +++++++++++++--- .../platformplayer/downloads/VideoDownload.kt | 78 +++++++++++-------- 2 files changed, 104 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index 826d8eb2..b2af4023 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -4,11 +4,12 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.content.res.Configuration import android.net.Uri import android.os.Bundle +import android.os.StrictMode +import android.os.StrictMode.VmPolicy import android.util.Log import android.util.TypedValue import android.view.View @@ -21,19 +22,45 @@ import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentContainerView import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import com.futo.platformplayer.* +import com.futo.platformplayer.BuildConfig +import com.futo.platformplayer.R +import com.futo.platformplayer.Settings +import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment -import com.futo.platformplayer.fragment.mainactivity.main.* +import com.futo.platformplayer.fragment.mainactivity.main.BrowserFragment +import com.futo.platformplayer.fragment.mainactivity.main.BuyFragment +import com.futo.platformplayer.fragment.mainactivity.main.ChannelFragment +import com.futo.platformplayer.fragment.mainactivity.main.CommentsFragment +import com.futo.platformplayer.fragment.mainactivity.main.ContentSearchResultsFragment +import com.futo.platformplayer.fragment.mainactivity.main.CreatorSearchResultsFragment +import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment +import com.futo.platformplayer.fragment.mainactivity.main.DownloadsFragment +import com.futo.platformplayer.fragment.mainactivity.main.HistoryFragment +import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment +import com.futo.platformplayer.fragment.mainactivity.main.ImportPlaylistsFragment +import com.futo.platformplayer.fragment.mainactivity.main.ImportSubscriptionsFragment +import com.futo.platformplayer.fragment.mainactivity.main.MainFragment +import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment +import com.futo.platformplayer.fragment.mainactivity.main.PlaylistSearchResultsFragment +import com.futo.platformplayer.fragment.mainactivity.main.PlaylistsFragment +import com.futo.platformplayer.fragment.mainactivity.main.PostDetailFragment +import com.futo.platformplayer.fragment.mainactivity.main.RemotePlaylistFragment +import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment +import com.futo.platformplayer.fragment.mainactivity.main.SourcesFragment +import com.futo.platformplayer.fragment.mainactivity.main.SubscriptionGroupFragment +import com.futo.platformplayer.fragment.mainactivity.main.SubscriptionGroupListFragment +import com.futo.platformplayer.fragment.mainactivity.main.SubscriptionsFeedFragment +import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment +import com.futo.platformplayer.fragment.mainactivity.main.TutorialFragment +import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment +import com.futo.platformplayer.fragment.mainactivity.main.WatchLaterFragment import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.GeneralTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment @@ -41,7 +68,15 @@ import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFrag import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.ImportCache -import com.futo.platformplayer.states.* +import com.futo.platformplayer.setNavigationBarColorAndIcons +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateBackup +import com.futo.platformplayer.states.StateDeveloper +import com.futo.platformplayer.states.StatePayment +import com.futo.platformplayer.states.StatePlatform +import com.futo.platformplayer.states.StatePlayer +import com.futo.platformplayer.states.StatePlaylists +import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.SubscriptionStorage import com.futo.platformplayer.stores.v2.ManagedStore @@ -49,15 +84,22 @@ import com.futo.platformplayer.views.ToastView import com.futo.polycentric.core.ApiMethods import com.google.gson.JsonParser import com.google.zxing.integration.android.IntentIntegrator -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import java.io.File import java.io.PrintWriter import java.io.StringWriter import java.lang.reflect.InvocationTargetException -import java.util.* +import java.util.LinkedList +import java.util.Queue import java.util.concurrent.ConcurrentLinkedQueue + class MainActivity : AppCompatActivity, IWithResultLauncher { //TODO: Move to dimensions @@ -154,6 +196,15 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { } constructor() : super() { + if (BuildConfig.DEBUG) { + StrictMode.setVmPolicy( + VmPolicy.Builder() + .detectLeakedClosableObjects() + .penaltyLog() + .build() + ) + } + ApiMethods.UserAgent = "Grayjay Android (${BuildConfig.VERSION_CODE})"; Thread.setDefaultUncaughtExceptionHandler { _, throwable -> diff --git a/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt b/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt index bd4e9ee3..debcd660 100644 --- a/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt +++ b/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt @@ -540,15 +540,20 @@ class VideoDownload { Logger.i(TAG, "Download '$name' segment $index Sequential"); val segmentFile = File(context.cacheDir, "segment-${UUID.randomUUID()}") - segmentFiles.add(segmentFile) + val outputStream = segmentFile.outputStream() + try { + segmentFiles.add(segmentFile) - val segmentLength = downloadSource_Sequential(client, segmentFile.outputStream(), segment.uri) { segmentLength, totalRead, lastSpeed -> - val averageSegmentLength = if (index == 0) segmentLength else downloadedTotalLength / index - val expectedTotalLength = averageSegmentLength * (variantPlaylist.segments.size - 1) + segmentLength - onProgress(expectedTotalLength, downloadedTotalLength + totalRead, lastSpeed) + val segmentLength = downloadSource_Sequential(client, outputStream, segment.uri) { segmentLength, totalRead, lastSpeed -> + val averageSegmentLength = if (index == 0) segmentLength else downloadedTotalLength / index + val expectedTotalLength = averageSegmentLength * (variantPlaylist.segments.size - 1) + segmentLength + onProgress(expectedTotalLength, downloadedTotalLength + totalRead, lastSpeed) + } + + downloadedTotalLength += segmentLength + } finally { + outputStream.close() } - - downloadedTotalLength += segmentLength } Logger.i(TAG, "Combining segments into $targetFile"); @@ -757,8 +762,10 @@ class VideoDownload { var lastSpeed: Long = 0; val result = client.get(url); - if (!result.isOk) + if (!result.isOk) { + result.body?.close() throw IllegalStateException("Failed to download source. Web[${result.code}] Error"); + } if (result.body == null) throw IllegalStateException("Failed to download source. Web[${result.code}] No response"); @@ -766,38 +773,41 @@ class VideoDownload { val sourceStream = result.body.byteStream(); var totalRead: Long = 0; - var read: Int; + try { + var read: Int; + val buffer = ByteArray(4096); - val buffer = ByteArray(4096); + do { + read = sourceStream.read(buffer); + if (read < 0) + break; - do { - read = sourceStream.read(buffer); - if (read < 0) - break; + fileStream.write(buffer, 0, read); - fileStream.write(buffer, 0, read); + totalRead += read; - totalRead += read; + readSinceLastSpeedTest += read; + if (totalRead.toDouble() / progressRate > lastProgressCount) { + onProgress(sourceLength, totalRead, lastSpeed); + lastProgressCount++; + } + if (readSinceLastSpeedTest > speedRate) { + val lastSpeedTime = timeSinceLastSpeedTest; + timeSinceLastSpeedTest = System.currentTimeMillis(); + val timeSince = timeSinceLastSpeedTest - lastSpeedTime; + if (timeSince > 0) + lastSpeed = (readSinceLastSpeedTest / (timeSince / 1000.0)).toLong(); + readSinceLastSpeedTest = 0; + } - readSinceLastSpeedTest += read; - if (totalRead.toDouble() / progressRate > lastProgressCount) { - onProgress(sourceLength, totalRead, lastSpeed); - lastProgressCount++; - } - if (readSinceLastSpeedTest > speedRate) { - val lastSpeedTime = timeSinceLastSpeedTest; - timeSinceLastSpeedTest = System.currentTimeMillis(); - val timeSince = timeSinceLastSpeedTest - lastSpeedTime; - if (timeSince > 0) - lastSpeed = (readSinceLastSpeedTest / (timeSince / 1000.0)).toLong(); - readSinceLastSpeedTest = 0; - } + if (isCancelled) + throw CancellationException("Cancelled"); + } while (read > 0); + } finally { + sourceStream.close() + result.body.close() + } - if (isCancelled) - throw CancellationException("Cancelled"); - } while (read > 0); - - lastSpeed = 0; onProgress(sourceLength, totalRead, 0); return sourceLength; }