由于发行方自己整合了其他平台的 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 设置中之后再打包。这个 gradle 文件的位置在 :

Unity 安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\GradleTemplates 需要注意的是,由于不同版本 Unity 使用的 gradle 不一样,所以如果进行版本迁移/引擎升级时需要重新确认下当前版本 Unity 下的这个 gradle 文件中的内容,如果和已经配置好的不同则需要进行重新配置。

同时,Unity 也允许我们自定义 build.gradle 文件,但是需要把其放入项目的 Plugins/Android 目录下。为了稳妥起见,我将 Unity 自带的 3 个文件一起复制到了 Plugins/Android 文件夹中,并进行修改。

Androidstudio广告展示 android接入广告sdk_Unity 对接安卓 sdk


其中只修改了 mainTemplate.gradle 这个问题件,其他两个貌似作用不大,只是为了保险起见所以也挪过来了。

需要注意的是 mainTemplate.gradle 这个文件是项目级的 build.gradle 模板,libTemplate.gradle 是模块级的 build.gradle 模板。

这样,在使用 gradle 构建 apk 的时候就会优先读取 Plugins/Android 目录下面的这个 mainTemplate.gradle 文件了。修改的地方如下图所示:

Androidstudio广告展示 android接入广告sdk_远程依赖广告_02


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

效果图:

Androidstudio广告展示 android接入广告sdk_Unity 对接安卓 sdk_03


6、应用开启时白屏

这个问题应该是由于自己定义了 AndroidManifest 文件导致的,大概的情况如下图所示:

Androidstudio广告展示 android接入广告sdk_ Unity 广告对接_04


在开启应用的一瞬间会有一个白屏展示,由于我的手机这个白屏很快就消失了所以不太好截图,试了几次最后截了个正在消失中的效果,可以看到整个背景是白色然后附带了一个应用标题。

如果单纯使用 Unity 打包一个不含自定义 AndroidManifest 文件的安卓的 apk 是不会产生这种情况的。

由于对安卓开发并不了解,所以目前我只能提供个解决方法。这个问题的产生是和 AndroidManifest 文件中的 android:theme标签有关,而解决方法可以尝试先使用 Unity 的空工程导出一个安卓工程,然后对比里面的 AndroidManifest 文件的差异。

<application android:theme="@style/UnityThemeSelector" 
  			   android:icon="@drawable/app_icon" 
  			   android:label="@string/app_name" 
  			   android:isGame="true" 
  			   android:banner="@drawable/app_banner">

以上是 Unity 空工程导出的安卓工程中的 AndroidManifest 文件节选,可以看到里面对 theme 进行了设置,而我们需要做的就是照葫芦画瓢,把这个 theme 的设置复制粘贴到自己的 AndroidManifesst 文件中就可以了,但是后面的 style 是什么呢?经过查找在 AndroidProject\src\main\res\values 目录下可以发现一个叫做 “styles.xml” 的文件,这里面就有对于 UnityThemeSelector 的描述:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="UnityThemeSelector" parent="BaseUnityTheme">
	<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="BaseUnityTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
</style>
<style name="UnityThemeSelector.Translucent" parent="@style/UnityThemeSelector">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>

可以看出其中第一个 style 就是我们的目标,而后面跟了个 parent 就是下面第二个 style,所以最好也带着,而第三个貌似就没有用了,直接删除,最后的文件如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="UnityThemeSelector" parent="BaseUnityTheme">
	<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="BaseUnityTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
</style>
</resources>

把这个文件放到自己的安卓工程的相应目录下 src\main\res\values,之后打 aar 包就可以了。经过测试,此时打开应用的效果就是直接一个全屏黑色的效果,然后就会很快进入应用闪屏画面了。