最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

webview - Memory Usage in Android App Keeps Increasing Over Time (Others Category in Profiler) - Stack Overflow

programmeradmin2浏览0评论

I am developing an Android app that primarily focuses on:

  1. Fetching details from an API.

  2. Receiving updates via MQTT.

  3. Displaying content based on the received data, including:

    • Videos using ExoPlayer .

    • Images using Glide .

    • Websites and YouTube videos using WebView .

  4. Sending displayed content details to OpenSearch.

When the app starts, the memory usage is around 200 MB as observed in the Android Profiler. However, after running the app for several hours, the memory usage keeps increasing. The profiler shows no memory leaks (0 leaks), but the "Others" category in the memory graph continues to grow over time. I have attached the dominator tree from the heap dump which I have taken after my app running for 3 hours.

  1. How can I identify what is causing the increase in the "Others" category in the Android Profiler?

  2. Are there any best practices for managing memory in apps that use ExoPlayer, Glide, WebView, and MQTT?

  3. Could this issue be related to native memory allocations (e.g., bitmaps, file handles) rather than Java heap memory?

Any guidance or suggestions on how to debug and resolve this issue would be greatly appreciated!

 @SuppressLint("SetTextI18n", "SetJavaScriptEnabled")
@OptIn(UnstableApi::class)
fun startLoop() {
    // If this partition has video content initially, set up ExoPlayer and attach PlayerView
    if (hasVideoInitially) {
        val loadControl = DefaultLoadControl.Builder()
            .setBufferDurationsMs(
                2000, 2000, 1000, 2000
            )
            .build()

        val renderersFactory = DefaultRenderersFactory(context)
            .setEnableDecoderFallback(true)

        exoPlayer =
            ExoPlayer.Builder(context, renderersFactory)
                .setLoadControl(loadControl)
                .build()
        playerView = PlayerView(context).apply {
            useController = false
            setShutterBackgroundColor(Color.TRANSPARENT)
            layoutParams = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT
            )
        }
        playerView?.player = exoPlayer
        partitionContainer.addView(playerView)
    }

    // Create an ImageView for images/PDF
    imageView = ImageView(context).apply {
        layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT
        )
        scaleType = ImageView.ScaleType.FIT_XY
        visibility = GONE
    }
    partitionContainer.addView(imageView)

    // Create a WebView for HTML content
    webView = WebView(context).apply {
        layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT
        )
        visibility = GONE
        settings.javaScriptEnabled = true
        settings.domStorageEnabled = true
        settings.cacheMode = WebSettings.LOAD_NO_CACHE
        settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
        WebView.setWebContentsDebuggingEnabled(true)
    }
    partitionContainer.addView(webView)

    // Create a "No Content" TextView but hide it initially
    noContentTextView = TextView(context).apply {
        text = "No content to play."
        textSize = 18f
        setTextColor(Color.CYAN)
        gravity = Gravity.CENTER
        visibility = GONE
    }
    partitionContainer.addView(noContentTextView)

    // Kick off the main loop
    scope.launch {
        while (isActive) {
            try {
                // Take a "snapshot" of the current contents under the lock
                val snapshot = contentLock.withLock { contentList }

                if (snapshot.isEmpty()) {
                    // If we've *never* played content before, it means we're just waiting for content
                    // so do nothing special. If we have played content before, show "No content."
                    if (hasEverPlayedContent) {
                        // Show the "No content" message
                        noContentTextView?.visibility = VISIBLE
                        noContentTextView?.bringToFront()
                    }
                    delay(2000)
                } else if (snapshot.size == 1 && snapshot[0].contentType.lowercase(Locale.ROOT) == "widget") {

                    // Display the widget once
                    displayWidgetIndefinitely(snapshot[0])
                    // Then break out of the while loop or block the loop.
                    break
                } else {
                    // Hide "No content" message if it was visible
                    noContentTextView?.visibility = GONE

                    // Loop over the snapshot
                    for (item in snapshot) {
                        if (!isActive) break  // If our scope got canceled mid-loop
                        if (!hasFiredFirstContentPlayed) {
                            hasFiredFirstContentPlayed = true
                            onFirstContentPlayed?.onFirstContentPlayed()
                        }
                        hasEverPlayedContent = true
                        displayContent(item)
                    }
                }
            } catch (e: Exception) {
                FirebaseCrashlytics.getInstance().recordException(e)
            }
        }
    }
}

I am developing an Android app that primarily focuses on:

  1. Fetching details from an API.

  2. Receiving updates via MQTT.

  3. Displaying content based on the received data, including:

    • Videos using ExoPlayer .

    • Images using Glide .

    • Websites and YouTube videos using WebView .

  4. Sending displayed content details to OpenSearch.

When the app starts, the memory usage is around 200 MB as observed in the Android Profiler. However, after running the app for several hours, the memory usage keeps increasing. The profiler shows no memory leaks (0 leaks), but the "Others" category in the memory graph continues to grow over time. I have attached the dominator tree from the heap dump which I have taken after my app running for 3 hours.

  1. How can I identify what is causing the increase in the "Others" category in the Android Profiler?

  2. Are there any best practices for managing memory in apps that use ExoPlayer, Glide, WebView, and MQTT?

  3. Could this issue be related to native memory allocations (e.g., bitmaps, file handles) rather than Java heap memory?

Any guidance or suggestions on how to debug and resolve this issue would be greatly appreciated!

 @SuppressLint("SetTextI18n", "SetJavaScriptEnabled")
@OptIn(UnstableApi::class)
fun startLoop() {
    // If this partition has video content initially, set up ExoPlayer and attach PlayerView
    if (hasVideoInitially) {
        val loadControl = DefaultLoadControl.Builder()
            .setBufferDurationsMs(
                2000, 2000, 1000, 2000
            )
            .build()

        val renderersFactory = DefaultRenderersFactory(context)
            .setEnableDecoderFallback(true)

        exoPlayer =
            ExoPlayer.Builder(context, renderersFactory)
                .setLoadControl(loadControl)
                .build()
        playerView = PlayerView(context).apply {
            useController = false
            setShutterBackgroundColor(Color.TRANSPARENT)
            layoutParams = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT
            )
        }
        playerView?.player = exoPlayer
        partitionContainer.addView(playerView)
    }

    // Create an ImageView for images/PDF
    imageView = ImageView(context).apply {
        layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT
        )
        scaleType = ImageView.ScaleType.FIT_XY
        visibility = GONE
    }
    partitionContainer.addView(imageView)

    // Create a WebView for HTML content
    webView = WebView(context).apply {
        layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT
        )
        visibility = GONE
        settings.javaScriptEnabled = true
        settings.domStorageEnabled = true
        settings.cacheMode = WebSettings.LOAD_NO_CACHE
        settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
        WebView.setWebContentsDebuggingEnabled(true)
    }
    partitionContainer.addView(webView)

    // Create a "No Content" TextView but hide it initially
    noContentTextView = TextView(context).apply {
        text = "No content to play."
        textSize = 18f
        setTextColor(Color.CYAN)
        gravity = Gravity.CENTER
        visibility = GONE
    }
    partitionContainer.addView(noContentTextView)

    // Kick off the main loop
    scope.launch {
        while (isActive) {
            try {
                // Take a "snapshot" of the current contents under the lock
                val snapshot = contentLock.withLock { contentList }

                if (snapshot.isEmpty()) {
                    // If we've *never* played content before, it means we're just waiting for content
                    // so do nothing special. If we have played content before, show "No content."
                    if (hasEverPlayedContent) {
                        // Show the "No content" message
                        noContentTextView?.visibility = VISIBLE
                        noContentTextView?.bringToFront()
                    }
                    delay(2000)
                } else if (snapshot.size == 1 && snapshot[0].contentType.lowercase(Locale.ROOT) == "widget") {

                    // Display the widget once
                    displayWidgetIndefinitely(snapshot[0])
                    // Then break out of the while loop or block the loop.
                    break
                } else {
                    // Hide "No content" message if it was visible
                    noContentTextView?.visibility = GONE

                    // Loop over the snapshot
                    for (item in snapshot) {
                        if (!isActive) break  // If our scope got canceled mid-loop
                        if (!hasFiredFirstContentPlayed) {
                            hasFiredFirstContentPlayed = true
                            onFirstContentPlayed?.onFirstContentPlayed()
                        }
                        hasEverPlayedContent = true
                        displayContent(item)
                    }
                }
            } catch (e: Exception) {
                FirebaseCrashlytics.getInstance().recordException(e)
            }
        }
    }
}
Share Improve this question edited Mar 7 at 10:27 karthik vs asked Mar 7 at 7:36 karthik vskarthik vs 12 bronze badges 2
  • what view is you show content? recyclerview or ohter? Did you called Exoplayer.release() and Webview.destory()? – 卡尔斯路西法 Commented Mar 7 at 9:14
  • I show content from list of contents in Loop. – karthik vs Commented Mar 7 at 10:14
Add a comment  | 

1 Answer 1

Reset to default 0

well,the partitionContainer view only addView and not called removeView, jvm can't recycle object.

if constantly addview ,the memory usage wall continue to increase.

if you called removeView in other function,did you called Exoplayer.release() and Webview.destory() while call removeView?

I can't foound Other type in your picture,and can you add app starts memory usage picture?

发布评论

评论列表(0)

  1. 暂无评论