Unity项目接入Android的Admob Native(原生视频) SDK(二、AS导出aar)
2. 修改build.gradle文件,有两个不同的这个文件,都需要修改
2.1 在指定位置添加 maven { url "https://maven.google.com" }
4. 在 libs 文件夹下添加 unity-classes.jar 包
文件路径:Unity安装路径\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar
6. 在MainActivity脚本中加入广告逻辑
6.1 添加引用(类似Unity的引入命名空间)
6.2 修改基类,继承 "UnityPlayerActivity",删除或注释掉Android的默认显示界面(具体代码见文末)
7. 到这里,SDK已经接的差不多了,为了让AS导出aar包,而不是直接导出Apk,还需要修改几个地方
将 AndroidMainfest.xml 文件中的相对路径,改为绝对路径,避免导到Unity后因为找不到Android文件报错
修改后
8.1 删除 AndroidMainfest.xml 文件中的 "android:theme= ··· ···" 语句,删除 style.xml 文件
8.2 删除 AndroidMainfest.xml 中的 "roundIcon" 语句
9. 修改 build.gradle 文件
9.1 修改 'com.android.application' 为 'com.android.library'
9.2 删除 applicationId
10. 看一下 MianAcivity 脚本,如果有一下报错,提示缺少权限,在 AndroidMainfest.xml 中的指定位置加入语句:
"<uses-permission android:name="android.permission.INTERNET" />"
11. 编译一下没有报错就可以导出aar包了
12. 到这里已经完成了AS导出aar的操作
因为AS自动下载的Native SDK并不在项目里,而且不会自动打包到导出的aar包里,需要手动找到SDK的aar文件,与项目导出aar一起添加到Unity工程
在AS的External Libraries可以看到用到的SDK的aar
在下面标注的路径下分别找到SDK用到的aar文件,共有8个(Windows和Mac路径差不多)
找到后先复制到一个文件夹,全部找齐后一起导入Unity(共8个aar)
13.1 创建Unity工程,这里继续使用了Unity项目接入Android的Admob Native(原生视频广告) SDK(一)里的Unity工程
13.2 将 AS 工程里的 AndroidMainfest.xml ,AS导出的aar(app-debug),找到的8个SDK aar 文件一起导入Unity项目 Plugins/Android 文件夹下
13.3 在AS导出的aar(app-debug)中,libs文件夹下有一个 unity-classes.jar(在AS中导入的Unity jar包),因为Unity导出Apk时会自动加入这个jar包,避免因为jar包重复打包出错,需要删除该文件。用打包软件打开jar包,删除unity-classes.jar
14. 设置 Player Settings
其中Minimum API Level 要与AS工程里的 "minSdkVersion" 一致
15. 开始打包,这时候并不会一次成功,检查一下报错,如果是以下报错,因为 "$" 符号报错,这是因为在AS里用的相对路径,这个写法在Unity中不识别
找到导入的aar文件,解压软件打开xml文件,找到报错的一行,将其修改为Unity的Package Name + "mobileadsinitprovider"(忽略划掉的那个红色线框,标记错了)
16. 现在OK了,打包出Apk,测一下如果可以正常显示广告视频,恭喜这就OK了,后面的步骤可以跳过了。
17. 如果视频不显示,如下图:
还是有问题,继续改~ 视频不显示的原因也很简单,就是在 AndroidMainfest.xml 里面有一个 "hardwareAccelerated" 属性,设为true就好了,原因简单,改起来却非常麻烦。因为Unity打包Apk的时候,会自动合并所有的 AndroidMainfest.xml 文件,"hardwareAccelerated" 属性会被自动改为false,也就是说,即使手动将该属性值修改为true,在导出Apk的时候,Unity还是会将其改为false。
不清楚在AS能不能修改后锁定该属性,禁止在Unity中进行修改,或者其他有效的方式,如果有最好了。限于目前没有发现有效的方法,只能再次将Unity工程导出到AS,在AS里修改该属性,然后出Apk。
17.1 导出AS工程,接下来的操作,可以与之前Unity项目接入Android的Admob Native(原生视频广告) SDK(一)差不多,可以参照
17.2 如果有这个报错,检查Android Plugin Version是否是2.3.0,如果是,改为3.2.0
17.3 看一下报错原因,点击蓝色链接自动更新修复
17.4 将 AndroidMainfest.xml 里的 "hardwareAccelerated" 属性设为 true
<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>
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 MainActivity extends UnityPlayerActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } // ----- 在后面加入代码 ----- 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; // AdLoader.Builder builder = new AdLoader.Builder(this, anUnitId); // // builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() { //// @Override // public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) { // // Show the ad. // nativeAd = unifiedNativeAd; // adLoaded = true; // // videoStatus.setText("load native ad successful"); // } // }); // // VideoOptions videoOptions = new VideoOptions.Builder() // //是否静音状态开始 // .setStartMuted(false) // .build(); // // NativeAdOptions adOptions = new NativeAdOptions.Builder() // .setVideoOptions(videoOptions) // .build(); // // builder.withNativeAdOptions(adOptions); // // AdLoader adLoader = builder.withAdListener(new AdListener() { // @Override // public void onAdFailedToLoad(int errorCode) { // // Handle the failure by logging, altering the UI, and so on. // onNativeLoadedFail(); // } // }).build(); // // adLoader.loadAd(new AdRequest.Builder().build()); ////// 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()); } //显示广告 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); } }