0
点赞
收藏
分享

微信扫一扫

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)

Admob的Native(原生)广告SDK有Unity版本,但只支持图文广告,不支持视频,为了在Native中加入视频,只好来接Android的SDK。实现了Unity项目导出Android Studio工程,AS接入Android SDK后导出Apk过程。

对于Android小白,这无疑是一个痛苦的过程,遇到了诸多问题,在此记录一下详细的入坑过程。还没能做到AS生成aar包导入到Unity调用,期待大佬秀操作了~~ 每个项目可能发生的问题也不相同,而且Android基础为零,虽然实现了功能,也难免存在一些问题,仅限于参考,还要以实际问题为主。

1. 先创建Unity项目

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob

2. 创建一个脚本,用来调用Android的函数,要注意的是,调用Android的Java接口是直接用的函数名,这里需要预先定义好所需要的函数名,Unity导出AS工程后再修改会很麻烦。

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");
}
}

3. 修改包名,导出AS包

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_谷歌原生广告_02Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_03

4. 接下来就是Android Studio的操作了,用AS打开Unity导出的项目,后续将在下图红色线框内标记的地方进行修改

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Native_04

5. 修改AS工程,在Admob官方给出的演示里也有说明,不过只划了重点,没有详细过程,庆幸的是,有官方Demo可以参照

说明: ​​https://developers.google.com/admob/android/native-unified​​

Demo: ​​https://github.com/googleads/googleads-mobile-android-examples/releases/tag/5.3​​

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_05

5.1 修改"build.gradle"文件,在下图标记位置添加代码,自动下载引用Android的Admob SDK

maven {
url "https://maven.google.com"
}
implementation 'com.google.android.gms:play-services-ads:17.0.0'

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Android_06

5.2 在"AndroidManifest.xml"文件中添加Admob的AppId,现在用的是测试Id,正式项目发布时需要换成正式Id

<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Android_07

5.3 在"src\main\res"路径下创建"layout"文件夹,将官方Demo里相同路径的"ad_unified.xml" Android UI布局文件拖进来,可以尝试简单的修改一下相对位置、对齐方式等布局,如果对Android比较熟,也可以自己写布局文件,Demo布局文件如下:

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Native_08Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_09

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

5.4 如果有下图的报错,尝试改一下Android Plugin Version(貌似是2.3.0版本有问题,我改用了3.2.0)

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_10

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_谷歌原生广告_11

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Android_12

6. 如果现在还报错,正常~~,SDK等修改或添加的文件还没更新到本地

6.1 点击蓝色的报错,会自动下载更新缺失的文件

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_13

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_14

6.2 提示需要将"AndroidManifest.xml"文件中的"minSdkVersion"属性移到"build.gradle"文件中,点击蓝色提示自动移动了,但这里有个问题,自动添加到"build.gradle"的"minSdkVersion"会多一个"=",需要手动删除"="

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_谷歌原生广告_15

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_16

6.3 提示"compile"函数名已经弃用,使用"implementation"替代Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_17

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Native_18Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_谷歌原生广告_19

6.4 刷新一下,还有一个警告,提醒将"AndroidManifest.xml"文件中的"targetSdkVersion"属性移到"build.gradle"文件中,如果build文件已有这个属性了,直接将AndroidManifest里的删除就好了

 Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Android_20

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_谷歌原生广告_21

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_22

7. 之前在测试的时候,到这里已经改的差不多了,广告也可以正常加载到,唯独视频不显示(暂定、静音按钮都有),尝试着改了好多地方都不行,无奈之下与Android的Demo一一对比改过的文件,最后才发现在"AndroidManifest.xml"中有一个叫作"hardwareAccelerated"(硬件加速)的属性,设为"true"就可以正常显示视频了Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_23

8. 到这里修改的地方都改的差不多了,接下来就是最重要的一步,在"UnityPlayerActivity"脚本里加入Native广告的逻辑,提供Unity调用的函数接口(函数名在Unity项目里就需要定义好,现在在AS里加入函数逻辑)

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_24

8.1 引入命名空间(按照Unity的叫法了···)

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_25

8.2 原有的代码不需要修改,在脚本后面继续添加广告逻辑即可,我的广告逻辑如下,具体怎么写要看需求了,重点在于获取到Native广告的返回值后,将其展示到Android的UI界面,这里参照官方Demo做了一下修改:

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_26

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);
}
}

9. 现在已经把所有的工作都做完了,刷新一下AS,如果没有报错的话,就可以打包Apk啦

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Unity_27

打包完成后,点击打包成功的提示,打开"locate"路径就看到Apk啦,完美~~~

Unity项目接入Android的Admob Native(原生视频广告) SDK(一)_Admob_28

后续:AS接好Native SDK,导出aar到UnityUnity项目接入Android的Admob Native(原生视频广告) SDK(二、AS导出aar)

Unity项目接入Android的Admob Native(原生视频广告) SDK(二、AS导出aar)


举报

相关推荐

0 条评论