ConstraintLayout 中的 MoPubRecyclerAdapter 和 Facebook 原生广告崩溃

Posted

技术标签:

【中文标题】ConstraintLayout 中的 MoPubRecyclerAdapter 和 Facebook 原生广告崩溃【英文标题】:Crash with MoPubRecyclerAdapter and Facebook Native Ads in ConstraintLayout 【发布时间】:2020-07-25 13:06:38 【问题描述】:

预期

MoPubRecyclerAdapter 预计会使用定义的 ConstraintLayout 扩充原生 Facebook RecyclerView 单元格。

观察到

错误 对于由 Facebook 原生广告创建的 ConstraintLayouts,MoPubRecyclerAdapter 会间歇性崩溃。此问题已在 MoPub SDK forum 和 MoPub android Mediation GitHub 存储库中记录。

日志


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_
    android:layout_
    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_
        android:layout_
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@id/native_media_view"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/sponsored"
        android:layout_
        android:layout_
        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_
        android:layout_
        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_
        android:layout_
        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 ->

环境

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 级别

8.1.0 9 10

设备

LG Q60 像素 3a 红米 Note 5 Pro

尝试的解决方案

MoPub SDK 的库版本已更新为5.12.0,Facebook Audience Network 更新为5.8.0,Facebook 中介更新为5.8.0.0。是否能解决上述崩溃问题,有待确定。

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'

【问题讨论】:

【参考方案1】:

使用RelativeLayout 而不是ConstraintLayout

正如 MoPub 的工程师在此 GitHub issue 中指出的那样,Facebook 中介尚不兼容 ConstraintLayout

由于 Facebook Audience Network SDK 4.99.0+ 版本的内部更改,AdChoices 图标 XML 视图(ID 为 native_ad_choices_relative_layout)需要为 RelativeLayout。适配器在这里需要RelativeLayout

之前

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/native_ad_choices_relative_layout"
        android:layout_
        android:layout_
        android:gravity="left"
        app:layout_constraintBottom_toBottomOf="@id/native_cta"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@id/native_cta" />

之后

    <RelativeLayout
        android:id="@+id/native_ad_choices_relative_layout"
        android:layout_
        android:layout_
        android:gravity="left" />

文档:Setup Ad Renderers for Native Ads

示例:github.com/mopub/mopub-sdk-android

【讨论】:

以上是关于ConstraintLayout 中的 MoPubRecyclerAdapter 和 Facebook 原生广告崩溃的主要内容,如果未能解决你的问题,请参考以下文章

MoPub #withMediatedNetworkConfiguration 解释

如何使用适用于 iOS 的 Admob (google) 设置 MoPub 中介?

MoPub 横幅广告

MoPub 替代品

如何在 android studio 中添加 Mopub 广告

在 Android 中设置 MoPub 广告