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

发表于2018-11-11
评论5 6.2k浏览
前面的Unity项目接入Android的Admob Native(原生视频广告) SDK(一)实现了利用Unity导出AS工程,在AS里接入Android的Admob Native广告(可以播放视频,Unity SDK不支持视频),然后打包Apk。这样已经实现了将Native广告加入到Unity项目,但仍有一个小问题,就是每次出包,都需要导出AS工程,再在AS里接好SDK,然后再打包。每次出包都要接一次SDK,这显然是很不可取的方式。最好的做法是将Native SDK在AS里接好,然后导出一个aar包,在项目需要用到时,导入Unity,加入Native广告,不影响以后的出包方式,可谓是一劳永逸,而且可以将这个导入aar文件在C#里封装好接口,导出一个package,后续需要的项目都可以直接使用了,非常方便。

还是要再说明一下,本人Android小白,对AS/Android/Java了解太少,在网上也没找到合适的案例,虽然实现了功能,过程中难免会出现一些问题。现在看整个过程也很简单,按照流程用不了几十分钟就能搞定,不过从一无所知到实现需求,从零到一,中间的坑也只有遇到了才能体会到无助,虽然过程很痛苦,收获也更多,分享出来,仅供大家参考,有任何不对的地方欢迎指正。另外,非常感谢同事的帮助,才有如今的成果。

下面进入正题:与Unity项目接入Android的Admob Native(原生视频广告) SDK(一)(https://blog.csdn.net/qq_39108767/article/details/83830632)有很多类似的操作

1. 创建一个空的AS工程:

创建完成进入AS界面,主要将在下面这几个地方进行修改

2. 修改build.gradle文件,有两个不同的这个文件,都需要修改

2.1 在指定位置添加  maven { url "https://maven.google.com" }

2.2 在指定位置添加 implementation 'com.google.android.gms:play-services-ads:17.0.0'  (另一个文件)
3. 修改 AndroidMainfest.xml 文件
在指定位置添加  <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
添加  <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/>  (这里用测试Id)


4. 在 libs 文件夹下添加 unity-classes.jar 包

文件路径:Unity安装路径\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar

5. 添加Admob Native官方给出Demo中的Native Ad UI布局文件(代码见文末),如果对Android熟悉,可以修改或自己定义该文件
Native说明: https://developers.google.com/admob/android/native-unified
Demo: https://github.com/googleads/googleads-mobile-android-examples/releases/tag/5.3
加一个id


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

17.5 看报错原因,继续修改
等编译没有问题,就全部搞定了,打包Apk,大功告成,OKOKOK

上面提到,需要将AS下载的SDK的8个aar文件,手动添加到Unity项目中,还有一种方法,直接导入Admob的Unity SDK,里面已经包含了这8个aar文件。如果项目只需要Native广告,可以选择上面提到的方式,手动找到AS将SDK下载到本地的aar,添加到Unity;如果项目还需要加入Admob的banner等其他广告,则需要导入Admob的Unity SDK。

--------------------- 

ad_unified.xml 布局文件
<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>

MainActivity.java 脚本
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);
    }
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引