androidkotlinandroid-recyclerviewapplovin

While loading MaxNative Ad into recyclerView it gives error "Spicified view already has parent. soYou should call removeView on parent's reference?


I am writing code to laod MaxNative ad from Applovins into recyclerView. I load it first time and use it for every new call. here is the code

fun showNativeAd2(adContainer: FrameLayout) {
        val params = adContainer.layoutParams
        params.height = 300.px

        adContainer.layoutParams = params
        if (isPremium()) {
            adContainer.visibility = View.GONE
            return
        }
        adContainer.visibility = View.VISIBLE
        val nativeAdLoader = MaxNativeAdLoader(adMobIds.nativeId, adContainer.context)

        nativeAdLoader.setNativeAdListener(object : MaxNativeAdListener() {

            override fun onNativeAdLoaded(nativeAdView: MaxNativeAdView?, ad: MaxAd) {
                // Clean up any pre-existing native ad to prevent memory leaks.
                if (nativeAd != null) {
                    nativeAdLoader.destroy(nativeAd)
                }
                nativeAd = ad
                globalNativeAd = nativeAdView

                adContainer.removeAllViews()
                adContainer.addView(nativeAdView)
            }

            override fun onNativeAdLoadFailed(adUnitId: String, error: MaxError) {
                // We recommend retrying with exponentially higher delays up to a maximum delay
                log("$adUnitId  $error")
            }

            override fun onNativeAdClicked(ad: MaxAd) {
                log("$ad")
            }
        })
        if (globalNativeAd == null)
            nativeAdLoader.loadAd()
        else{
            adContainer.removeAllViews()
            adContainer.addView(globalNativeAd)
        }
    } 

here globalNative is global variable that is initialized only one's on first call of function if globalNative is null and for each new call it just shows that which is stored in globalNative variable. but while loading so recyclerview shows error.

here is stacktrace.

2023-04-27 12:15:29.952 10265-10265/com.thesrb.bluewords E/CustomActivityOnCrash: The previous app process crashed. This is the stack trace of the crash:
    java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.view.ViewGroup.addViewInner(ViewGroup.java:5269)
        at android.view.ViewGroup.addView(ViewGroup.java:5090)
        at android.view.ViewGroup.addView(ViewGroup.java:5030)
        at android.view.ViewGroup.addView(ViewGroup.java:5003)
        at com.thesrb.bluewords.ads_libs.AppLovinUtils.showNativeAd2(AppLovinUtils.kt:161)
        at com.thesrb.bluewords.adapter.WordAdapter.setAds(WordAdapter.kt:217)
        at com.thesrb.bluewords.adapter.WordAdapter.onBindViewHolder(WordAdapter.kt:188)
        at com.thesrb.bluewords.adapter.WordAdapter.onBindViewHolder(WordAdapter.kt:32)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6296)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1631)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)

Solution

  • I have created an extension function (in kotlin) to avoid such crashes and it is working for me perfectly.

    Add this function into any file or Object type class

    fun ViewGroup.addCleanView(view: View?) {
        (view?.parent as? ViewGroup)?.removeView(view)
        this.removeAllViews()
        this.addView(view)
    }
    

    Now, usage is simple as follow:

    adContainer.addCleanView(globalNativeAd)
    

    here too,

    adContainer.addCleanView(nativeAdView)
    

    No need to remove views or etc. my extension function will handle all.