The specified child already has a parent. You must call removeView() on the child's parent first.

789 views
Skip to first unread message

邱至民

unread,
May 10, 2020, 6:49:32 AM5/10/20
to Google Mobile Ads SDK Developers
Hi, 

Here's the error logs.

android.view.ViewGroup.addViewInner (ViewGroup.java:5034)
android.view.ViewGroup.addView (ViewGroup.java:4865)
android.view.ViewGroup.addView (ViewGroup.java:4805)
android.view.ViewGroup.addView (ViewGroup.java:4778)
com.google.android.gms.ads.internal.formats.ah.a (ah.java:44)
com.google.android.gms.ads.internal.formats.client.k.a (k.java:25)
fa.onTransact (fa.java:4)
android.os.Binder.transact (Binder.java:667)
com.google.android.gms.internal.ads.zzgc.zza (zzgc.java:21)
com.google.android.gms.internal.ads.zzacx.zza (zzacx.java:16)
com.google.android.gms.ads.formats.UnifiedNativeAdView.setNativeAd (UnifiedNativeAdView.java:52)
com.ihad.ptt.model.handler.AdMobHelper$BannerItemHolder.setNativeAd (AdMobHelper.java:911)
com.ihad.ptt.model.handler.AdMobBannerAdHelper.bind (AdMobBannerAdHelper.java:156)
com.ihad.ptt.ArticleContentRecyclerAdapter$SponsorAdMobItemHolder.updateAd (ArticleContentRecyclerAdapter.java:3871)
com.ihad.ptt.ArticleContentRecyclerAdapter.onBindViewHolder (ArticleContentRecyclerAdapter.java:1624)
com.ihad.ptt.ArticleContentRecyclerAdapter.onBindViewHolder (ArticleContentRecyclerAdapter.java:58)
androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder (RecyclerView.java:7065)
androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder (RecyclerView.java:7107)
androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline (RecyclerView.java:6012)
androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:6279)
androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline (GapWorker.java:288)
androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline (GapWorker.java:345)
androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline (GapWorker.java:361)
androidx.recyclerview.widget.GapWorker.prefetch (GapWorker.java:368)
androidx.recyclerview.widget.GapWorker.run (GapWorker.java:399)
android.os.Handler.handleCallback (Handler.java:873)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loop (Looper.java:280)
android.app.ActivityThread.main (ActivityThread.java:6706)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)


I tried to display the same nativeAd in every row of the recyclerView but still can't reproduce this problem.
So it's not about how we add adView to our layout.
The only thing you'll add to the view dynamically is the AdChoices overlay.
Maybe there are some leak issues inside your SDK.

Thanks.

Mobile Ads SDK Forum Advisor Prod

unread,
May 10, 2020, 11:02:34 PM5/10/20
to kiwa...@gmail.com, google-adm...@googlegroups.com

Hi there,

 

Thank you for reaching out to us.

 

I'm afraid that using native ads in a RecyclerView is a bit more involved than just adding and removing views to and from a view parent. You may check out our example code for further details on this.

 

Regards,

Ziv Yves Sanchez

Mobile Ads SDK Team



ref:_00D1U1174p._5004Q1zL9Bs:ref

邱至民

unread,
May 11, 2020, 5:24:30 AM5/11/20
to Google Mobile Ads SDK Developers
Hi, 

I've already followed the document.
Here's how I inflate layout.

protected BannerItemHolder inflateBanner(){
Activity activity = getActivity();
if( activity == null ) return null;

UnifiedNativeAdView view = (UnifiedNativeAdView) LayoutInflater.from(activity).inflate(R.layout.admob_native_banner_body_item, null);

return new BannerItemHolder(view);
}


protected CardItemHolder inflateCard(){
Activity activity = getActivity();
if( activity == null ) return null;

UnifiedNativeAdView view = (UnifiedNativeAdView) LayoutInflater.from(activity).inflate(R.layout.admob_native_card_item, null);

return new CardItemHolder(view);
}

Here are my ItemHolders.

    public static class BannerItemHolder implements NativeItemHolder {

UnifiedNativeAdView adView;

@BindView(R.id.icon)
ImageView icon;

@BindView(R.id.headline)
NativeAdHeaderView headline;

@Nullable
@BindView(R.id.advertiser)
NativeAdAdvertiserView advertiser;

@Nullable
@BindView(R.id.body)
NativeAdBodyView body;

@Nullable
@BindView(R.id.socialContext)
TextView socialContext;

@BindView(R.id.callToAction)
Button callToAction;

public BannerItemHolder(UnifiedNativeAdView adView) {
ButterKnife.bind(this, adView);
this.adView = adView;
}

public void setHeadline(String headline){
this.headline.setText(headline);
this.headline.setTextSize(SingletonProperty.getInstance().getListItemTextSize());
adView.setHeadlineView(this.headline);
}

public void setBody(String body){
if( this.body == null ) return;
this.body.setText(body);
this.body.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 3);
adView.setBodyView(this.body);
}

public void setAdvertiser(String advertiser){
this.advertiser.setText(advertiser);
this.advertiser.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 4);
adView.setAdvertiserView(this.headline);
}

public void setSocialContext(String socialContext){
if( this.socialContext == null ) return;
if( socialContext == null || socialContext.isEmpty() ){
this.socialContext.setVisibility(View.GONE);
}
else {
this.socialContext.setText(socialContext);
this.socialContext.setVisibility(View.VISIBLE);
this.socialContext.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 5);

}
}

public void setIcon(NativeAd.Image icon){
if( icon == null ) {
this.icon.setVisibility(View.GONE);
return;
}
else {
this.icon.setVisibility(View.VISIBLE);
}

this.icon.setImageDrawable(icon.getDrawable());
adView.setIconView(this.icon);
}

public void setCallToAction(String callToAction){
this.callToAction.setText(callToAction);
this.callToAction.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 5);
adView.setCallToActionView(this.callToAction);
}

public void setNativeAd(UnifiedNativeAd ad){
this.adView.setNativeAd(ad);
}

@Override
public void destroy() {
if( this.adView == null ) return;
this.adView.destroy();
}
}

public static class CardItemHolder implements NativeItemHolder {

UnifiedNativeAdView adView;

@BindView(R.id.media)
MediaView media;

@BindView(R.id.icon)
ImageView icon;

@BindView(R.id.headline)
NativeAdHeaderView headline;

@Nullable
@BindView(R.id.advertiser)
NativeAdAdvertiserView advertiser;

@Nullable
@BindView(R.id.socialContext)
TextView socialContext;

@Nullable
@BindView(R.id.body)
NativeAdBodyView body;

@BindView(R.id.callToAction)
Button callToAction;

public CardItemHolder(UnifiedNativeAdView adView) {
ButterKnife.bind(this, adView);
this.adView = adView;
this.media.setImageScaleType(ImageView.ScaleType.FIT_CENTER);
this.media.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
if (child instanceof ImageView) {
ImageView imageView = (ImageView) child;
imageView.setAdjustViewBounds(true);

Resources resources = imageView.getResources();
if( resources == null ) return;

int maxHeight = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 400, imageView.getResources().getDisplayMetrics() );
imageView.setMaxHeight(maxHeight);
}
}

@Override
public void onChildViewRemoved(View parent, View child) {}
});
}

public void setHeadline(String headline){
this.headline.setText(headline);
this.headline.setTextSize(SingletonProperty.getInstance().getListItemTextSize());
adView.setHeadlineView(this.headline);
}

public void setBody(String body){
if( this.body == null ) return;
this.body.setText(body);
this.body.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 3);
adView.setBodyView(this.body);
}

public void setAdvertiser(String advertiser){
if( this.advertiser == null ) {
this.headline.setText(advertiser);
this.headline.setTextSize(SingletonProperty.getInstance().getListItemTextSize());
adView.setAdvertiserView(this.headline);
}
else {
this.advertiser.setText(advertiser);
this.advertiser.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 4);
adView.setAdvertiserView(this.headline);
}
}

public void setSocialContext(String socialContext){
if( this.socialContext == null ) return;
if( socialContext == null || socialContext.isEmpty() ){
this.socialContext.setVisibility(View.GONE);
}
else {
this.socialContext.setText(socialContext);
this.socialContext.setVisibility(View.VISIBLE);
this.socialContext.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 5);
}
}

public void setMedia(MediaContent media){
this.media.setMediaContent(media);
adView.setMediaView(this.media);
}

public void setIcon(NativeAd.Image icon){
if( icon == null ) {
this.icon.setVisibility(View.GONE);
return;
}
else {
this.icon.setVisibility(View.VISIBLE);
}

this.icon.setImageDrawable(icon.getDrawable());
adView.setIconView(this.icon);
}

public void setCallToAction(String callToAction){
this.callToAction.setText(callToAction);
this.callToAction.setTextSize(SingletonProperty.getInstance().getListItemTextSize() - 5);
adView.setCallToActionView(this.callToAction);
}

public void setNativeAd(UnifiedNativeAd ad){
this.adView.setNativeAd(ad);
}

@Override
public void destroy() {
if( this.adView == null ) return;
this.adView.destroy();
}
}

Here's how I set native ad.

    public void bind(ViewGroup parent){
if( remoteConfigHelper.isLimitedEdition() ) return;
if( property.isSubscriptionNoAds() ) return;
if( !property.isUseNativeAd() ) return;
if( parent == null ) return;
AdMobHelper.AdHolder adHolder = AdMobHelper.getInstance().getAd(AdMobHelper.Type.card);
if( adHolder == null ) return;
if( referencedAd != null && referencedAd.equals(adHolder) ) return;

if( AdMobHelper.getInstance().isNativeBannerOnly() ){
bindBanner(parent, adHolder);
}
else {
bindCard(parent, adHolder);
}
}

private void bindBanner(ViewGroup parent, AdMobHelper.AdHolder adHolder) {
AdMobHelper.BannerItemHolder itemHolder = AdMobHelper.getInstance().inflateBanner();

if( itemHolder == null ) return;

unbind(parent);

UnifiedNativeAd ad = adHolder.ad;

itemHolder.setHeadline(ad.getHeadline());
itemHolder.setAdvertiser(ad.getAdvertiser());
itemHolder.setBody(ad.getBody());

itemHolder.setIcon(ad.getIcon());
itemHolder.setCallToAction(ad.getCallToAction());
itemHolder.setNativeAd(ad);

parent.addView(itemHolder.adView);

this.referencedAd = adHolder;

adHolder.setImpression(true);
}

private void bindCard(ViewGroup parent, AdMobHelper.AdHolder adHolder) {
AdMobHelper.CardItemHolder itemHolder = AdMobHelper.getInstance().inflateCard();

if( itemHolder == null ) return;

unbind(parent);

UnifiedNativeAd ad = adHolder.ad;

itemHolder.setMedia(ad.getMediaContent());
itemHolder.setIcon(ad.getIcon());
itemHolder.setHeadline(ad.getHeadline());
itemHolder.setAdvertiser(ad.getAdvertiser());

Bundle extras = ad.getExtras();
if (extras != null && extras.containsKey(FacebookAdapter.KEY_SOCIAL_CONTEXT_ASSET)) {
String socialContext = extras.getString(FacebookAdapter.KEY_SOCIAL_CONTEXT_ASSET);
itemHolder.setSocialContext(socialContext);
}
else {
itemHolder.setSocialContext(null);
}

itemHolder.setBody(ad.getBody());
itemHolder.setCallToAction(ad.getCallToAction());
itemHolder.setNativeAd(ad);

parent.addView(itemHolder.adView);

this.referencedAd = adHolder;
this.referenceItemHolder = new WeakReference<>(itemHolder);

adHolder.setImpression(true);
}

Here's the ItemHolder in RecyclerVIew.

@Override
public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch ( viewType ){
        case SPONSOR_ADMOB:{
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.admob_recycler_item, parent, false);

// create ViewHolder
return new SponsorAdMobItemHolder(itemLayoutView);
}
        // ...
    }
}

public static class SponsorAdMobItemHolder extends ItemHolder {

AdMobBannerAdHelper adMobBannerAdHelper = new AdMobBannerAdHelper();
ViewGroup view;

public SponsorAdMobItemHolder(View itemView){
super(itemView, null, null);

ButterKnife.bind(this, itemView);

this.view = (ViewGroup) itemView;
}

@Override
void updateAd(Context context){
adMobBannerAdHelper.bind(view);
}

@Override
void detach() {
super.detach();
adMobBannerAdHelper.unbind(view);
}
}

I inflate new native ad layout every time to simplify the codes.
And remove every view from parent every time before adding native ad to the recycler view.
Is there any issue in my implementation?
The error logs showed that the exception was thrown by UnifiedNativeAdView.setNativeAd().
I thought the problem is inside your SDK.

Thanks.

mobileadssdkforumadvisor於 2020年5月11日星期一 UTC+8上午11時02分34秒寫道:

Mobile Ads SDK Forum Advisor Prod

unread,
May 11, 2020, 5:49:30 AM5/11/20
to kiwa...@gmail.com, google-adm...@googlegroups.com

Hi there,

 

Thank you for updating us on your concern.

 

There doesn't seem to be any glaring issues with your code at first glance. That said, since it is proving to be quite a challenge just tracing code and theorizing code state, could you kindly provide the details below (via Reply privately to author) so that we can further investigate this issue?

  • Copy of a sample project (replicating the issue)
  • Steps to replicate the issue

邱至民

unread,
May 11, 2020, 9:38:02 AM5/11/20
to Google Mobile Ads SDK Developers
Hi, 

Could you change your way to add AdChoices overlay?
Just like letting us to include a imageView which could load this icon.
If there's no any view for AdChoices bound to UnifiedNativeAdView then you can add it later dynamically.
That will avoid this issue for good.

Thanks.

mobileadssdkforumadvisor於 2020年5月11日星期一 UTC+8下午5時49分30秒寫道:

Mobile Ads SDK Forum Advisor Prod

unread,
May 11, 2020, 1:54:26 PM5/11/20
to kiwa...@gmail.com, google-adm...@googlegroups.com
Hi,

I work with Ziv and have followed the conversation that you are having. I would reiterate what has previously been said that if it would be possible could we get the following 2 items. Share via "Reply privately to author"

  • Copy of a sample project (replicating the issue)
  • Steps to replicate the issue
Also from what it looks like, you were trying to get the same ad to appear in each spot of the RecyclerView but have not been able to get that to happen? Where is the recursive part of the code that allows for the different ads to be placed in?

I would also want to make sure that the Butterknife is being properly implemented. As I was looking over that too.

 

Regards,
William Pescherine

邱至民

unread,
May 13, 2020, 10:35:35 PM5/13/20
to Google Mobile Ads SDK Developers
Hi, 

I would also want to make sure that the Butterknife is being properly implemented. 
 
Butterknife shouldn't be a problem.
I use it everywhere.
  • Copy of a sample project (replicating the issue)
  • Steps to replicate the issue
As I don't know how to reproduce it, I only get these reports from Crashlytics.
Would that still be helpful with a sample project?

Also from what it looks like, you were trying to get the same ad to appear in each spot of the RecyclerView but have not been able to get that to happen?
 
I was trying to reproduce this issue by displaying the same ads in every item of the RecyclerView.
But it displayed well without any error. 

Where is the recursive part of the code that allows for the different ads to be placed in?
 
The different ads will be added to the nativeAdArray in OnUnifiedNativeAdLoadedListener.onUnifiedNativeAdLoaded.
And I'll always uses the newest one from the array.
So it's possible to display the same nativeAd in different Activities.

protected AdHolder getAd(@Type String type){
if( !initialized ) return null;
switch (type) {
case Type.banner: {
int size = bannerGarage.size();

if (size == 0) return null;

return bannerGarage.get(size - 1);
}
case Type.card: {
int size = cardGarage.size();

if (size == 0) return null;

return cardGarage.get(size - 1);
}
default:
return null;
}
}


Thanks.

mobileadssdkforumadvisor於 2020年5月12日星期二 UTC+8上午1時54分26秒寫道:

Mobile Ads SDK Forum Advisor Prod

unread,
May 13, 2020, 11:51:05 PM5/13/20
to kiwa...@gmail.com, google-adm...@googlegroups.com

Hi there,

 

Thank you for providing additional details on your concern.

 

I'm afraid that we would be needing a sample project with a consistently replicable set of steps in order to continue with the investigation. Kindly send one via Reply privately to author whenever you are able to.

 

As for your post regarding the AdChoices overlay: we can formalize this into a feature request if you would kindly expand on this, particularly on why you wish for this feature to be implemented.

 

Regards,

Ziv Yves Sanchez

邱至民

unread,
May 29, 2020, 2:11:37 AM5/29/20
to Google Mobile Ads SDK Developers
Hi, 

Now I'm sure you're leaking this attribution view.

Screen Shot 2020-05-29 at 2.07.37 PM.png


I'm debugging some view issues on my iOS app.
And I found that your iOS SDK is leaking this attribution view too. Not just on Android.
You should definitely change your way to add this icon.

Thanks.

mobileadssdkforumadvisor於 2020年5月14日星期四 UTC+8上午11時51分05秒寫道:

Mobile Ads SDK Forum Advisor Prod

unread,
May 29, 2020, 5:49:03 AM5/29/20
to kiwa...@gmail.com, google-adm...@googlegroups.com
Hi there,

Thank you for reporting this. Would you able to reproduce the issue using our sample app? If so, could you please provide us an instrument recording capturing the behavior?

Regards,
Teejay Pimentel
Reply all
Reply to author
Forward
0 new messages