Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MaterialShapeDrawable caused OOM #4128

Open
ArcherEmiya05 opened this issue Mar 30, 2024 · 1 comment
Open

MaterialShapeDrawable caused OOM #4128

ArcherEmiya05 opened this issue Mar 30, 2024 · 1 comment

Comments

@ArcherEmiya05
Copy link

Description: OOM with RecyclerView with MaterialCardView.

Expected behavior: No OOM

Source code:

XML layout

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_margin="@dimen/space_2"
    app:cardCornerRadius="5dp">

        <androidx.appcompat.widget.LinearLayoutCompat
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/postImage"
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:adjustViewBounds="true"
                android:src="@drawable/logo_placeholder"
                android:background="@color/colorWhiteDark_Primary" />

            <androidx.appcompat.widget.LinearLayoutCompat
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="@dimen/space_8">

                <com.google.android.material.textview.MaterialTextView
                    android:id="@+id/postTitle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingBottom="@dimen/space_4"
                    android:textColor="@color/colorPrimaryDark_Accent"
                    android:textSize="@dimen/textSize16"
                    android:maxLines="2"
                    android:textStyle="bold"
                    android:ellipsize="end"
                    tools:text="@tools:sample/full_names" />

                <com.google.android.material.textview.MaterialTextView
                    android:id="@+id/postTime"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center_vertical"
                    android:ellipsize="end"
                    android:maxLines="1"
                    android:textSize="@dimen/textSize12"
                    tools:text="@tools:sample/cities"/>

                <com.google.android.material.textview.MaterialTextView
                    android:id="@+id/postDescription"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/textSize12"
                    android:maxLines="2"
                    android:ellipsize="end"
                    android:paddingVertical="@dimen/space_4"
                    tools:text="@tools:sample/lorem/random" />

            </androidx.appcompat.widget.LinearLayoutCompat>

        </androidx.appcompat.widget.LinearLayoutCompat>

</com.google.android.material.card.MaterialCardView>

RecyclerView Adapter

class HorizontalNewsAdapter(
    private val glide: RequestManager,
    private val itemListener: ItemListener
) : FilterableListAdapter<WordPressPostDataDomain, HorizontalNewsAdapter.ItemView>(DiffUtilNews()) {

    inner class ItemView(itemView: TrendingCardBinding) : RecyclerView.ViewHolder(itemView.root) {
        private val titleTxtV: MaterialTextView = itemView.postTitle
        private val dateTxtV: MaterialTextView = itemView.postTime
        private val descriptionTxtV: MaterialTextView = itemView.postDescription
        private val featuredImage: AppCompatImageView = itemView.postImage

        // Full update/binding
        fun bindFull(domain: WordPressPostDataDomain) {

            with(domain) {

                bindTextData(titleDomain.formattedTitle, formattedDate, contentDomain.strippedImageTagContent)

                glide
                    .load(featuredMediaUrl)
                    .placeholder(itemView.context.resToDrawable(R.drawable.logo_placeholder))
                    .centerCrop()
                    .into(featuredImage)

                // It is advisable to always use interface when working with `setOnClickListener`
                // in adapter to avoid outdated binding and data reference.
                // Passing position and using `adapter.currentList[position]` in the Activity/Fragment
                // is better than declaring `getItem(position)` inside `setOnClickListener`
                // Reference: https://stackoverflow.com/q/77308368/12204620
                itemView.setOnClickListener {
                    itemListener.onItemSelected(bindingAdapterPosition)
                }

            }

        }

        // Partial update/binding
        fun bindPartial(bundle: Bundle) {
            bindTextData(
                bundle.getString(DiffUtilNews.ARG_NEWS_TITLE),
                bundle.getString(DiffUtilNews.ARG_NEWS_DATE),
                bundle.getString(DiffUtilNews.ARG_NEWS_STRIPPED_CONTENT)
            )
        }

        private fun bindTextData(title: String?, date: String?, description: String?) {

            titleTxtV.text = title
            dateTxtV.text = date
            descriptionTxtV.text = description

        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemView {
        return ItemView(
            TrendingCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        )
    }

    override fun onBindViewHolder(holder: ItemView, position: Int) {
        // Reference: https://forums.raywenderlich.com/t/speed-up-your-android-recyclerview-using-diffutil-raywenderlich-com/141338/8?u=archeremiya
        onBindViewHolder(holder, holder.bindingAdapterPosition, emptyList())
    }

    override fun onBindViewHolder(holder: ItemView, position: Int, payloads: List<Any>) {

        holder.apply {

            // For most cases `getBindingAdapterPosition()` is use as it provides the most up-to-date position considering pending changes.
            // It is also ideal for getting data from `getCurrentList()` as it returns the only position under this specific adapter and
            // not the entire adapters of a ConcatAdapter.
            // Reference: https://stackoverflow.com/a/63148812
            val domain = getItem(bindingAdapterPosition)

            if (payloads.isEmpty() || payloads.first() !is Bundle) {
                holder.bindFull(domain) // Full update/binding
            }
            else {
                val bundle = payloads.first() as Bundle
                holder.bindPartial(bundle) // Partial update/binding
            }

        }

    }

    // Required when setHasStableIds is set to true
    override fun getItemId(position: Int): Long {
        return currentList[position].id.toLong()
    }

    override fun onFilter(list: List<WordPressPostDataDomain>, constraint: String): List<WordPressPostDataDomain> {
        TODO("Not yet implemented")
    }

    fun interface ItemListener {

        fun onItemSelected(position: Int)

    }

}

OOM

05:47:18.336  W  Throwing OutOfMemoryError "Failed to allocate a 28 byte allocation with 3984 free bytes and 3KB until OOM" (recursive case)
05:47:18.338  W  "main" prio=5 tid=1 Runnable
05:47:18.338  W    | group="main" sCount=0 dsCount=0 obj=0x73c04710 self=0x7bce33ba00
05:47:18.338  W    | sysTid=3946 nice=-6 cgrp=default sched=0/0 handle=0x7bd184ffe8
05:47:18.338  W    | state=R schedstat=( 2083268596 213661445 6688 ) utm=183 stm=25 core=2 HZ=100
05:47:18.338  W    | stack=0x7fd8420000-0x7fd8422000 stackSize=8MB
05:47:18.338  W    | held mutexes= "mutator lock"(shared held)
05:47:18.338  W    at com.google.android.material.shape.MaterialShapeDrawable.<init>(MaterialShapeDrawable.java:126)
05:47:18.338  W    at com.google.android.material.shape.MaterialShapeDrawable.<init>(MaterialShapeDrawable.java:222)
05:47:18.338  W    at com.google.android.material.shape.MaterialShapeDrawable.<init>(MaterialShapeDrawable.java:213)
05:47:18.338  W    at com.google.android.material.card.MaterialCardViewHelper.<init>(MaterialCardViewHelper.java:143)
05:47:18.338  W    at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:174)
05:47:18.338  W    at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:160)
05:47:18.338  W    at java.lang.reflect.Constructor.newInstance!(Native method)
05:47:18.338  W    at android.view.LayoutInflater.createView(LayoutInflater.java:619)
05:47:18.338  W    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:764)
05:47:18.338  W    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
05:47:18.338  W    at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
05:47:18.338  W    at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
05:47:18.338  W    at com.sample.app.databinding.TrendingCardBinding.inflate(TrendingCardBinding.java:59)
05:47:18.338  W    at com.sample.app.presentation.adapters.HorizontalNewsAdapter.onCreateViewHolder(HorizontalNewsAdapter.kt:98)
05:47:18.338  W    at com.sample.app.presentation.adapters.HorizontalNewsAdapter.onCreateViewHolder(HorizontalNewsAdapter.kt:40)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7788)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6873)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6757)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6753)
05:47:18.338  W    at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2362)
05:47:18.338  W    at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1662)
05:47:18.338  W    at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1622)
05:47:18.338  W    at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:687)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4645)
05:47:18.338  W    at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:4022)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.measureChildBeforeLayout(LinearLayoutCompat.java:1401)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.measureVertical(LinearLayoutCompat.java:685)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.onMeasure(LinearLayoutCompat.java:575)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.measureChildBeforeLayout(LinearLayoutCompat.java:1401)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.measureVertical(LinearLayoutCompat.java:685)
05:47:18.338  W    at androidx.appcompat.widget.LinearLayoutCompat.onMeasure(LinearLayoutCompat.java:575)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at androidx.core.widget.NestedScrollView.measureChildWithMargins(NestedScrollView.java:1921)
05:47:18.338  W    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
05:47:18.338  W    at androidx.core.widget.NestedScrollView.onMeasure(NestedScrollView.java:640)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:760)
05:47:18.338  W    at com.google.android.material.appbar.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:100)
05:47:18.338  W    at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:2348)
05:47:18.338  W    at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:831)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
05:47:18.338  W    at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
05:47:18.338  W    at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
05:47:18.338  W    at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
05:47:18.338  W    at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
05:47:18.338  W    at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
05:47:18.338  W    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
05:47:18.338  W    at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
05:47:18.338  W    at android.view.View.measure(View.java:18788)
05:47:18.338  W    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
05:47:18.338  W    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216)
05:47:18.338  W    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
05:47:18.338  W    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
05:47:18.338  W    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
05:47:18.338  W    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
05:47:18.338  W    at android.view.Choreographer.doCallbacks(Choreographer.java:670)
05:47:18.338  W    at android.view.Choreographer.doFrame(Choreographer.java:606)
05:47:18.338  W    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
05:47:18.338  W    at android.os.Handler.handleCallback(Handler.java:739)
05:47:18.338  W    at android.os.Handler.dispatchMessage(Handler.java:95)
05:47:18.338  W    at android.os.Looper.loop(Looper.java:148)
05:47:18.338  W    at android.app.ActivityThread.main(ActivityThread.java:5417)
05:47:18.338  W    at java.lang.reflect.Method.invoke!(Native method)
05:47:18.338  W    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
05:47:18.338  W    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
05:47:18.338  W  

Minimal sample app repro: N/A

Android API version: N/A

Material Library version: N/A (Sorry can't remember when this crash due to OOM happen, I kept this log long ago to submit a report but forgot to do so but surely before or during v1.10.0)

Device: N/A

@ArcherEmiya05 ArcherEmiya05 changed the title [MaterialShapeDrawable] caused OOM MaterialShapeDrawable caused OOM Mar 30, 2024
@hunterstich
Copy link
Contributor

Hi @ArcherEmiya05. Thanks for filing an issue!

In this case, we'd really need a sample that reproduces the issue. Are you able to create a simple app that reproduces the crash and confirm that MaterialCardView is the one allocating an unreasonable amount of memory?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants