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.

enter image description here

enter image description here

  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)
            }
        }
    }
}
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. Commented Mar 7 at 10:14

1 Answer 1

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?

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.