记录:Unity 工程对接 Android SDK 聚合广告

由于发行方自己整合了其他平台的 sdk,所以我们的项目也对广告部分进行调整。之前是对接的 Google 的 admob sdk,这个 sdk 有专门的 unity 版本可供使用,所以整体还算比较顺利,同时还对接了 Unity 自身的广告 sdk。然而发行方新整合的 sdk 只有安卓版本,所以需要在 Android Studio 上面做一些工作。
以下为记录这次工作中遇到的问题和解决过程。方便以后查阅。

1、Android SDK 和 Gradle 版本问题

此处需要关注三个文件:

  1. 项目级的 build.gradle,需要注意的是这个文件中的 classpath 'com.android.tools.build:gradle:3.5.0'
  2. 模块中的 build.gradle,这个文件中需要注意 compileSdkVersion、buildToolsVersion、minSdkVersion、targetSdkVersion 的对应版本号,这里主要是配置 Android SDK 和 build tools ,需要保证已经下载了和配置的版本号相符的 sdk 或 tools 版本,否则也会报错。
    另外需要注意 minSdkVersion 最好要和之后 Unity 中使用的最小 sdk 版本保持一致。
  3. 文件 gradle-wrapper.properties。
    需要注意 distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip中的 gradle 版本号。

其中项目级的 build.gradle 和 gradle-wrapper.properties 中的 gradle 有一定的对应关系,也可以参考下这篇文章。而模块中的 build.gradle 里面的 SDK 和 Build-Tools 的版本号也需要注意是已经下载好的。

以上如果没有配置好就会报和 gradle 有关的错误。主要会在打包的时候出现这些问题。

我所使用的是最新版本的 Android Studio 3.5,这 3 个文件的相关配置如下所示:

  1. 项目级的 build.gradle。保持了默认配置。
buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
  1. 模块中的 build.gradle。修改了版本号,使用了 Android 9.0 (28) 版本的 sdk 和 28.0.3 版本的 Build-Tools。
android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"
    defaultConfig {
        applicationId "com.example.helloandroid"
        minSdkVersion 16
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
  1. gradle-wrapper.properties 文件。保持了默认配置。
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

2、打 aar 包

之前使用过 jar 包来进行 Unity 和 Android Studio 的对接,可以参考这篇文章。但是毕竟 aar 包是现在官方所推荐的方式,所以这次会采用 aar 包来进行对接。

前期步骤和生成 jar 包相同,在 build jar 包的同时,系统也会生成 aar 包。在 Android Studio 3.5 中的位置是:工程名\模块名\build\outputs\aar\
导出 aar 包后还需要一些额外操作:

  • aar 包是个压缩包,需要对其内部进行修改。
  • 在 Android Studio 3.5 中导出的 AndroidManifest.xml 的位置:工程名\模块名\build\intermediates\library_manifest\debug\
  • 上一步单独导出的 AndroidManifest.xml 文件需要修改包名,将包名和 Unity 项目包名统一 (在 Android Studio 端制作对接模块的时候可以不用过于注意包名,因为只需要后期修改 xml 文件就可以进行对接,但是如果是需要反复测试的话可以考虑一开始就把安卓工程的包名和 Unity 项目的包名统一,这样就可以不用每次调整都修改了)。
  • aar 包内部的 AndroidManifest.xml 文件不动包名,删除 aar 包内部 xml 的 Icon 和 Lable 属性。因为 aar 包中的 AndroidManifest 是一个分支文件,只表示了 aar 包中的设置。
  • aar 包里的 libs 文件夹下的 classes.jar 删掉并换成 aar 包根目录下的,这个操作和 jar 包的类似。
  • 将 AndroidManifest.xml 文件和 aar 包直接放入 Plugins\Android 目录下。

aar 包虽然也需要手动去修改一些东西,但是其优点是 aar 包已经将很多依赖项目都自动集成到一起了,不需要开发者自己去创建这些东西。


3、远程依赖问题

之前我所接触的 sdk 分为两种,一种是 Unity 可以直接使用的,也就是 .unitypackage 包的 sdk,另一种是里面有依赖 jar 包的安卓 sdk,其中 Unity 可以直接使用的当然是最为简单,直接根据文档写一下测试代码就可以了,一般都没有什么问题可以直接使用,而包含依赖包的安卓 sdk 也就是需要从 Android Studio 制作一个模块来对接,无论是 aar 包也好,jar 包也好,基本都是走一遍 jar 包制作流程就可以了,但是这次的不太一样。
原因是这次并没有提供 sdk,更准确的说是提供了一种远程依赖,操作方式就是在项目级的 build.gradle 中加入 maven 远程依赖地址:

allprojects {
    repositories {
        maven { url "https://xxx.xxxxx.com/xxx/xxxxx/" }
    }
}

然后在模块的 build.gradle 中引入依赖包。
在初次打包测试时,apk 直接崩溃,经过之后的查证,是由于平台方的 sdk 并没有被打入 aar 包导致的,经过一段查找和对接后,在参考了这篇资料后问题得到了解决。
大概意思就是 Unity 现在打包时有自己的 gradle 设置,所以我们需要把这些远程依赖的东西加入到 Unity 的 gradle 设置中之后再打包。
同时,Unity 也允许我们自定义 build.gradle 文件,但是需要把其放入项目的 Plugins/Android 目录下。为了稳妥起见,我将 Unity 自带的 3 个文件一起复制到了 Plugins/Android 文件夹中,并进行修改。
记录:Unity 工程对接 Android SDK 聚合广告
其中只修改了 mainTemplate.gradle 这个问题件,其他两个貌似作用不大,只是为了保险起见所以也挪过来了。
这样,在使用 gradle 构建 apk 的时候就会优先读取 Plugins/Android 目录下面的这个 mainTemplate.gradle 文件了。修改的地方如下图所示:
记录:Unity 工程对接 Android SDK 聚合广告
其中 maven 是根据文档需求在指定位置添加的,而前三个 compile 也是根据文档和实际需求添加的。由于 Unity 的 gradle 版本较低,所以使用 “compile”。最后一个 compile 'com.google.android.gms:play-services-ads:17.1.1' 是由于该平台提供的解决方案中没有激励视频的接口。。。所以还需要我们额外对接一下 google 的激励视频。
至此,打出来的 apk 包就可以正常开启,不会直接崩溃了。


4、Unity 画面和广告画面的显示问题

这个问题是在进行 banner 广告对接的时候出现的。因为 banner 广告部分的文档需要额外在安卓页面布局的 xml 里添加一些东西,也就是说要在对接用的模块中自己写一个 layout 文件。之前没有接触过页面方面的对接,所以我遇到的问题是调用新写的这个页面时,会完全覆盖住 Unity 的画面,导致屏幕一片白。
我虽然也查了一些方式但是都没有成功,后来经过和平台方的沟通,他们找到了一份参考资料。这份资料是以 Android 编程为出发点 (之前我都是以 Unity 开发为出发点进行查找),基本思路是将 Unity 的画面嵌入到 Android 程序中。

Android 端的代码如下:

public class MainActivity extends UnityPlayerActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);       
        setContentView(R.layout.activity_banner_ad);
        
       	Log.d("MainActivity onCreate:", "============= UnityPlayerActivity Start!!! ===========");
        // 接入 Unity 画面
        View playerView = mUnityPlayer.getView();
        LinearLayout ll = (LinearLayout)findViewById(R.id.unityViewLyaout);
        ll.addView(playerView);
    }   
}

xml 布局完整代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/unityViewLyaout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >
    </LinearLayout>

    <com.zero.mediation.ad.view.TAdView
        android:id="@+id/bannerview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

其中 LinearLayout 中的代码就是 Unity 画面的布局,而下面的 TAdView 就是官方文档中对接 banner 广告必须要添加的布局。


5、完整代码

此处只是进行了对接的测试工作,可以保证测试广告能够正常显示,并没有进行具体的广告逻辑的编写。
完整的 Android 端代码如下:

public class MainActivity extends UnityPlayerActivity {

	// google 的 Admob 广告管理,用来处理激励视频广告的调用
    private AdmobManager admobManager;
    // banner 广告管理
    private BannerManager bannerManager;
    // 插屏广告管理
    private InterstitialManager interstitialManager;
	
	// 按官方文档进行调用的参数,和 banner 广告有关
    private WrapTadView adview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
        setContentView(R.layout.activity_banner_ad);
		
		Log.d("MainActivity onCreate:", "============= UnityPlayerActivity Start!!! ===========");
        // 接入 Unity 画面
        View playerView = mUnityPlayer.getView();
        LinearLayout ll = (LinearLayout)findViewById(R.id.unityViewLyaout);
        ll.addView(playerView);

        adview = findViewById(R.id.bannerview);

		// 分别实例化管理类
        bannerManager = new BannerManager(this, adview);
        interstitialManager = new InterstitialManager(this);
        admobManager = new AdmobManager(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (adview != null) {
            adview.removeAllViews();
        }
        adview = null;
        bannerManager.onDestroy();
        interstitialManager.onDestroy();
        admobManager.onDestroy();
    }
	
	// Unity 端调用的方法,展示 banner 广告
    public void showBannerAd() {
        Log.d("SDK TEST: ", "======  Show Banner Ad!  =====");
        bannerManager.Show();
    }

	// Unity 端调用的方法,展示插屏广告
    public void showInterstitialAd()
    {
        Log.d("SDK TEST: ", "======  Show Interstitial Ad!  =====");
        interstitialManager.Show();
    }
    
	// Unity 端调用的方法,展示激励视频
    public void showRewardedAd()
    {
        Log.d("SDK TEST: ", "======  Show Rewarded Ad!  =====");
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                admobManager.Show();
            }
        });
    }
}

将 banner、插屏广告和激励视频分别进行了封装处理,在进行类的实例化时进行了相应的广告初始化工作,然后通过可以通过对应的 show 方法进行广告调用。

Unity 端的代码:

public class AdManager : MonoBehaviour
{
    AndroidJavaObject mJo;
    private void Start()
    {
        try
        {
            AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            mJo = jc.GetStatic<AndroidJavaObject>("currentActivity");
        }
        catch(Exception e)
        {
            Debug.LogWarning("Error: " + e.ToString());
        }
        finally
        {
            if (mJo == null)
                Debug.Log("Jo is null");
            else
                Debug.Log("Has Jo !!!!!!");
        }
    }
	
	// 调用 banner 广告
    public void BtnBannerAd()
    {
        Debug.Log("========== BtnBannerAd!!! ============");
        mJo.Call("showBannerAd");
    }
	
	// 调用插屏广告
    public void BtnInterstitialAd()
    {
        Debug.Log("========== BtnInterstitialAd!!! ============");
        mJo.Call("showInterstitialAd");
    }
	
	// 调用激励视频
    public void BtnRewardedAd()
    {
        Debug.Log("========== BtnRewardedAd!!! ============");
        mJo.Call("showRewardedAd");
    }
}

效果图:
记录:Unity 工程对接 Android SDK 聚合广告

上一篇:android – Gradle:如何在存储库中包含依赖项来输出aar文件


下一篇:Go语言宕机恢复(recover)——防止程序崩溃