Unity项目接入Android的Admob Native(原生视频广告) SDK
public class NativeAdController : MonoBehaviour { [SerializeField] Button showAdBtn; [SerializeField] Button hideAdBtn; AndroidJavaObject jo; void Start () { // Android的Java接口 AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); InitNative(); showAdBtn.onClick.AddListener(ShowNative); hideAdBtn.onClick.AddListener(HideNative); } void InitNative() { //调用有参方法 object[] objects = new object[] { "ca-app-pub-3940256099942544~3347511713", "ca-app-pub-3940256099942544/1044960115", false }; jo.Call("initNativeAd", objects); } void ShowNative() { //调用有返回值方法 if (jo.Call<bool>("isNativeReady")) jo.Call("showNativeAd"); } void HideNative() { //调用无参方法 jo.Call("hideNativeAd"); } }
说明: https://developers.google.com/admob/android/native-unified
Demo: https://github.com/googleads/googleads-mobile-android-examples/releases/tag/5.3
maven { url "https://maven.google.com"
}
implementation 'com.google.android.gms:play-services-ads:17.0.0'
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/>
<com.google.android.gms.ads.formats.UnifiedNativeAdView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/nativeAdView" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="#FFFFFF" android:gravity="bottom" android:minHeight="0dp" android:orientation="vertical" android:paddingLeft="0dp" android:paddingRight="0dp" android:paddingBottom="0dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="bottom" android:orientation="vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:paddingTop="3dp" android:paddingBottom="3dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/ad_app_icon" android:layout_width="40dp" android:layout_height="40dp" android:adjustViewBounds="true" android:paddingEnd="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="bottom" android:orientation="vertical"> <TextView android:id="@+id/ad_headline" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#0000FF" android:textSize="16sp" android:textStyle="bold" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/ad_advertiser" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="bottom" android:textSize="14sp" android:textStyle="bold" /> <RatingBar android:id="@+id/ad_stars" style="?android:attr/ratingBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:isIndicator="true" android:numStars="5" android:stepSize="0.5" /> </LinearLayout> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="bottom" android:orientation="vertical"> <TextView android:id="@+id/ad_body" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:layout_marginRight="20dp" android:textSize="12sp" /> <ImageView android:id="@+id/ad_image" android:layout_width="250dp" android:layout_height="175dp" android:layout_gravity="center_horizontal" android:layout_marginTop="0dp" /> <com.google.android.gms.ads.formats.MediaView android:id="@+id/ad_media" android:layout_width="250dp" android:layout_height="175dp" android:layout_gravity="center_horizontal" android:layout_marginTop="0dp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:orientation="horizontal" android:paddingTop="1dp" android:paddingBottom="0dp"> <TextView android:id="@+id/ad_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="5dp" android:paddingLeft="5dp" android:paddingEnd="5dp" android:paddingRight="5dp" android:textSize="12sp" /> <TextView android:id="@+id/ad_store" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="5dp" android:paddingLeft="5dp" android:paddingEnd="5dp" android:paddingRight="5dp" android:textSize="12sp" /> <Button android:id="@+id/ad_call_to_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:textSize="12sp" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout> </com.google.android.gms.ads.formats.UnifiedNativeAdView>
6. 如果现在还报错,正常~~,SDK等修改或添加的文件还没更新到本地
6.1 点击蓝色的报错,会自动下载更新缺失的文件
6.2 提示需要将"AndroidManifest.xml"文件中的"minSdkVersion"属性移到"build.gradle"文件中,点击蓝色提示自动移动了,但这里有个问题,自动添加到"build.gradle"的"minSdkVersion"会多一个"=",需要手动删除"="
6.3 提示"compile"函数名已经弃用,使用"implementation"替代
6.4 刷新一下,还有一个警告,提醒将"AndroidManifest.xml"文件中的"targetSdkVersion"属性移到"build.gradle"文件中,如果build文件已有这个属性了,直接将AndroidManifest里的删除就好了
7. 之前在测试的时候,到这里已经改的差不多了,广告也可以正常加载到,唯独视频不显示(暂定、静音按钮都有),尝试着改了好多地方都不行,无奈之下与Android的Demo一一对比改过的文件,最后才发现在"AndroidManifest.xml"中有一个叫作"hardwareAccelerated"(硬件加速)的属性,设为"true"就可以正常显示视频了
8. 到这里修改的地方都改的差不多了,接下来就是最重要的一步,在"UnityPlayerActivity"脚本里加入Native广告的逻辑,提供Unity调用的函数接口(函数名在Unity项目里就需要定义好,现在在AS里加入函数逻辑)
8.1 引入命名空间(按照Unity的叫法了···)
8.2 原有的代码不需要修改,在脚本后面继续添加广告逻辑即可,我的广告逻辑如下,具体怎么写要看需求了,重点在于获取到Native广告的返回值后,将其展示到Android的UI界面,这里参照官方Demo做了一下修改:
package com.NativeAdTestDemo.NativeAdTestDemo; import com.unity3d.player.*; import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.Bundle; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.Gravity; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.AdLoader; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.MobileAds; import com.google.android.gms.ads.VideoController; import com.google.android.gms.ads.VideoOptions; import com.google.android.gms.ads.formats.MediaView; import com.google.android.gms.ads.formats.NativeAd; import com.google.android.gms.ads.formats.NativeAdOptions; import com.google.android.gms.ads.formats.UnifiedNativeAd; import com.google.android.gms.ads.formats.UnifiedNativeAdView; import java.util.List; public class UnityPlayerActivity extends Activity { protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code // Setup activity layout @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); mUnityPlayer = new UnityPlayer(this); setContentView(mUnityPlayer); mUnityPlayer.requestFocus(); } //此处省略脚本原有代码 /*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } // ----- 在后面加入代码 ----- private String adUnitId; private boolean isVideoMute; private UnifiedNativeAd nativeAd; private boolean adLoaded; private boolean isShowAd; private UnifiedNativeAdView adView; FrameLayout.LayoutParams layoutParams; //初始化 public void initNativeAd(String appId, String nativeId, boolean isVideoStartMute) { // MobileAds.initialize(this, appId); MobileAds.initialize(this, "ca-app-pub-3940256099942544~3347511713"); // adUnitId = nativeId; adUnitId = "ca-app-pub-3940256099942544/1044960115"; isVideoMute = isVideoStartMute; isShowAd = false; layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.BOTTOM; requestNativeAd(); } //是否加载完成 public boolean isNativeReady() { if(adLoaded && nativeAd != null) return true; requestNativeAd(); return false; } //展示广告 public void showNativeAd() { if(isShowAd || !adLoaded || nativeAd == null) return; isShowAd = true; //显示界面 new Thread() { public void run() { //这是耗时操作,完成之后更新UI; runOnUiThread(new Runnable(){ @Override public void run() { if(adView!= null) mUnityPlayer.removeView(adView); adView = (UnifiedNativeAdView) getLayoutInflater().inflate(R.layout.ad_unified, null); populateUnifiedNativeAdView(nativeAd, adView); mUnityPlayer.addView(adView, layoutParams); } }); } }.start(); adLoaded = false; } //隐藏广告 public void hideNativeAd() { if (!isShowAd) return;; isShowAd = false; adLoaded = false; //移除界面 new Thread() { public void run() { runOnUiThread(new Runnable(){ @Override public void run() { if(adView != null) { mUnityPlayer.removeView(adView); if(nativeAd != null) nativeAd.destroy(); adView = null; } } }); } }.start(); requestNativeAd(); } //加载成功回调 public void onNativeLoadedSuccess() { Toast.makeText(UnityPlayerActivity.this, "load native ad successful", Toast.LENGTH_SHORT).show(); } //加载失败回调 public void onNativeLoadedFail() { Toast.makeText(UnityPlayerActivity.this, "Failed to load native", Toast.LENGTH_SHORT).show(); } //视频播放完成回调 public void onNativeVideoEnd() { Toast.makeText(UnityPlayerActivity.this, "Video play end", Toast.LENGTH_SHORT).show(); } //---------- //请求广告 private void requestNativeAd() { adLoaded = false; VideoOptions videoOptions = new VideoOptions.Builder() .setStartMuted(isVideoMute) .build(); NativeAdOptions adOptions = new NativeAdOptions.Builder() .setVideoOptions(videoOptions) .build(); AdLoader adLoader = new AdLoader.Builder(this, adUnitId) .forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() { @Override public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) { nativeAd = unifiedNativeAd; adLoaded = true; onNativeLoadedSuccess(); } } ) .withAdListener(new AdListener() { @Override public void onAdFailedToLoad(int errorCode) { onNativeLoadedFail(); } }) .withNativeAdOptions(adOptions) .build(); adLoader.loadAd(new AdRequest.Builder().build()); } //显示广告,根据Native返回值,显示到Android界面 private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) { // Get the video controller for the ad. One will always be provided, even if the ad doesn't // have a video asset. //获取视频控制器 VideoController vc = nativeAd.getVideoController(); MediaView mediaView = (MediaView)adView.findViewById(R.id.ad_media); ImageView mainImageView = adView.findViewById(R.id.ad_image); //是否包含视频资源 if (vc.hasVideoContent()) { //事件 vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() { @Override public void onVideoEnd() { //播放结束 onNativeVideoEnd(); super.onVideoEnd(); } }); adView.setMediaView(mediaView); mainImageView.setVisibility(View.GONE); //adView.getMediaView().setBackgroundColor(Color.rgb(1,1,1)); } else { adView.setImageView(mainImageView); mediaView.setVisibility(View.GONE); // At least one image is guaranteed. List<NativeAd.Image> images = nativeAd.getImages(); mainImageView.setImageDrawable(images.get(0).getDrawable()); } adView.setHeadlineView(adView.findViewById(R.id.ad_headline)); adView.setBodyView(adView.findViewById(R.id.ad_body)); adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action)); adView.setIconView(adView.findViewById(R.id.ad_app_icon)); adView.setPriceView(adView.findViewById(R.id.ad_price)); adView.setStarRatingView(adView.findViewById(R.id.ad_stars)); adView.setStoreView(adView.findViewById(R.id.ad_store)); adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser)); // Some assets are guaranteed to be in every UnifiedNativeAd. ((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline()); ((TextView) adView.getBodyView()).setText(nativeAd.getBody()); ((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction()); // These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to // check before trying to display them. if (nativeAd.getIcon() == null) { adView.getIconView().setVisibility(View.GONE); } else { ((ImageView) adView.getIconView()).setImageDrawable( nativeAd.getIcon().getDrawable()); adView.getIconView().setVisibility(View.VISIBLE); } if (nativeAd.getPrice() == null) { adView.getPriceView().setVisibility(View.INVISIBLE); } else { adView.getPriceView().setVisibility(View.VISIBLE); ((TextView) adView.getPriceView()).setText(nativeAd.getPrice()); } if (nativeAd.getStore() == null) { adView.getStoreView().setVisibility(View.INVISIBLE); } else { adView.getStoreView().setVisibility(View.VISIBLE); ((TextView) adView.getStoreView()).setText(nativeAd.getStore()); } if (nativeAd.getStarRating() == null) { adView.getStarRatingView().setVisibility(View.INVISIBLE); } else { ((RatingBar) adView.getStarRatingView()) .setRating(nativeAd.getStarRating().floatValue()); adView.getStarRatingView().setVisibility(View.VISIBLE); } if (nativeAd.getAdvertiser() == null) { adView.getAdvertiserView().setVisibility(View.INVISIBLE); } else { ((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser()); adView.getAdvertiserView().setVisibility(View.VISIBLE); } adView.setNativeAd(nativeAd); } }