The MoPubRecyclerAdapter
is expected to inflate native Facebook RecyclerView
cells using a defined ConstraintLayout
.
Error
The MoPubRecyclerAdapter
is crashing intermittently for ConstraintLayout
s created from Facebook native ads. This issue has been noted in the MoPub SDK forum and MoPub Android Mediation GitHub repository.
Log
Fatal Exception: java.lang.ClassCastException: androidx.constraintlayout.widget.ConstraintLayout cannot be cast to android.widget.RelativeLayout
at com.mopub.nativeads.FacebookAdRenderer$FacebookNativeViewHolder.fromViewBinder(FacebookAdRenderer.java:139)
at com.mopub.nativeads.FacebookAdRenderer.renderAdView(FacebookAdRenderer.java:58)
at com.mopub.nativeads.FacebookAdRenderer.renderAdView(FacebookAdRenderer.java:28)
at com.mopub.nativeads.NativeAd.renderAdView(NativeAd.java:166)
at com.mopub.nativeads.MoPubStreamAdPlacer.bindAdView(MoPubStreamAdPlacer.java:433)
at com.mopub.nativeads.MoPubRecyclerAdapter.onBindViewHolder(MoPubRecyclerAdapter.java:424)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:286)
at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:343)
at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:359)
at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:366)
at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:397)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
facebook_native_ad_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/native_outer_view"
style="@style/AdContentCardStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/cell_content_feed_height"
android:textDirection="locale">
<TextView
android:id="@+id/native_title"
style="@style/CellCreatorStyle"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/native_icon_image" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@id/native_media_view"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/sponsored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sponsored"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/guideline" />
<com.facebook.ads.AdIconView
android:id="@+id/native_icon_image"
android:layout_width="@dimen/native_icon_image_dimen"
android:layout_height="@dimen/native_icon_image_dimen"
android:paddingRight="@dimen/padding_tiny"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.facebook.ads.MediaView
android:id="@+id/native_media_view"
style="@style/AdCellPreviewImageStyle"
android:contentDescription="@string/native_main_image"
app:layout_constraintBottom_toTopOf="@id/native_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/sponsored" />
<TextView
android:id="@+id/native_text"
style="@style/CellTitleStyle"
app:layout_constraintBottom_toTopOf="@id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/native_media_view"
tools:text="@string/learn_more" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left"
app:layout_constraintBottom_toBottomOf="@id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/native_cta" />
<TextView
android:id="@+id/native_cta"
style="@style/NativeCtaStyle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/native_text"
tools:text="@string/learn_more" />
</androidx.constraintlayout.widget.ConstraintLayout>
SomeFragment.kt
adapter = FeedAdapter(feedViewModel, viewEvent)
moPubAdapter = MoPubRecyclerAdapter(
requireActivity(),
adapter,
MoPubNativeAdPositioning.MoPubServerPositioning())
moPubAdapter.registerAdRenderer(FacebookAdRenderer(
FacebookViewBinder.Builder(fb_native_ad_item)
.titleId(native_title)
.textId(native_text)
.mediaViewId(native_media_view)
.adIconViewId(native_icon_image)
.adChoicesRelativeLayoutId(native_ad_choices_relative_layout)
.advertiserNameId(native_title)
.callToActionId(native_cta)
.build()))
val viewBinder = ViewBinder.Builder(native_ad_item)
.titleId(native_title)
.textId(native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(native_icon_image)
.callToActionId(native_cta)
.privacyInformationIconImageId(string.native_privacy_information_icon_image)
.build()
moPubAdapter.registerAdRenderer(FlurryNativeAdRenderer(FlurryViewBinder(Builder(viewBinder))))
moPubAdapter.registerAdRenderer(MoPubVideoNativeAdRenderer(
MediaViewBinder.Builder(fb_native_ad_item)
.mediaLayoutId(native_media_view)
.iconImageId(native_icon_image)
.titleId(native_title)
.textId(native_text)
.privacyInformationIconImageId(native_ad_choices_relative_layout)
.build()))
moPubAdapter.registerAdRenderer(MoPubStaticNativeAdRenderer(viewBinder))
moPubAdapter.setContentChangeStrategy(MOVE_ALL_ADS_WITH_CONTENT)
contentRecyclerView.adapter = moPubAdapter
FeedApater.kt
@ExperimentalCoroutinesApi
class FeedAdapter(val viewModel: FeedViewModel, val viewEvent: FeedViewEvent)
: PagedListAdapter<Content, FeedAdapter.ViewHolder>(DIFF_CALLBACK) {
class ViewHolder(private var binding: CellContentBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(viewModel: FeedViewModel, content: Content, onClickListener: OnClickListener) {
binding.viewModel = viewModel
binding.data = content
binding.clickListener = onClickListener
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = CellContentBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
getItem(position)?.let { content ->
holder.bind(viewModel, content, createOnClickListener(content, position))
}
}
private fun createOnClickListener(content: Content, position: Int) = OnClickListener { view ->
}
Libraries
implementation("com.mopub:mopub-sdk-native-static:5.11.1@aar") { transitive = true }
implementation("com.mopub:mopub-sdk-native-video:5.11.1@aar") { transitive = true }
implementation 'com.facebook.android:audience-network-sdk:5.1.0'
implementation 'com.mopub.mediation:facebookaudiencenetwork:5.1.0.0'
implementation 'com.flurry.android:ads:12.1.0@aar'
implementation 'com.flurry.android:analytics:12.1.0@aar'
implementation 'com.mopub.mediation:flurry:11.4.0.0'
Android levels
Devices
The library versions have been updated for the MoPub SDK to 5.12.0
, the Facebook Audience Network to 5.8.0
, and Facebook mediation to 5.8.0.0
. It is to be determined whether this resolves the above crash.
implementation("com.mopub:mopub-sdk-native-static:5.12.0") { transitive = true }
implementation("com.mopub:mopub-sdk-native-video:5.12.0") { transitive = true }
implementation 'com.facebook.android:audience-network-sdk:5.8.0'
implementation 'com.mopub.mediation:facebookaudiencenetwork:5.8.0.0'
RelativeLayout
instead of ConstraintLayout
As MoPub's engineer points out in this GitHub issue, Facebook mediation is not yet compatible with ConstraintLayout
.
AdChoices icon XML view (with the ID
native_ad_choices_relative_layout
) needs to be aRelativeLayout
due to internal changes in the 4.99.0+ version of the Facebook Audience Network SDK. The adapter expects aRelativeLayout
here.
Before
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left"
app:layout_constraintBottom_toBottomOf="@id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/native_cta" />
After
<RelativeLayout
android:id="@+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left" />
Documentation: Setup Ad Renderers for Native Ads