The MoPubRecyclerAdapter is expected to inflate native Facebook RecyclerView cells using a defined ConstraintLayout.
Error
The MoPubRecyclerAdapter is crashing intermittently for ConstraintLayouts 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 ConstraintLayoutAs 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 aRelativeLayoutdue to internal changes in the 4.99.0+ version of the Facebook Audience Network SDK. The adapter expects aRelativeLayouthere.
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