Unity3D中使用Gradle出包

开篇

本篇博客讲述如何在Unity3D中使用Gradle方式发布Android包。包含的内容一,出包的两种途径,一种是通过AndroidStudio导出aar到Unity,然后通过Unity打出Android包,一种是通过Unity导出AndroidStudio工程,然后通过AndroidStudio打出Android包。
二,Android端java代码和Unity端C#代码的相互调用。

本篇博客知识要求,对于Android Studio,Gradle,Unity3d有一定的了解。

环境&工具

Windows10 64位
Android Studio 3.5
Unity 2017.4.37f1(64bit)

使用Gradle

在Unity比较新的版本中,当切换到Android平台进行开发时,默认会使用gradle方式。可以通过 File -> Build Settings 打开Build Settings窗口查看。
Unity Build Settings窗口

如果发布的android包没有特殊需求,不需要改动android端的java代码,那么就可以直接在unity中发布android包即可,不过现实中每个项目或多或少都有对应平台的特殊修改,最常见的就是接入SDK,这个时候就需要修改android端的java代码了,同时还要和unity中的C#做交互。当然有的SDK因为功能特性,只提供封装好的jar包和一些C#源码,不需要添加java代码也可以接入。

创建 Android Studio 工程

首先讲创建一个Android Studio 工程,然后导出 aar 给 Unity 使用的方式。
可以创建一个 Empty Activity 类型的 Android Studio 工程,工程创建完毕后默认的app module可以不做调整修改,这个module我们是不需要的,所以上面选 Basic Activity 类型也可以的。
选择一个工程类型
然后在这个工程中再创建一个 Android Library 类型的 module。其实也可以修改上面的 app module 类型为 Library 。我图方便就直接创建一个 Library 类型的 module 了。
创建 Android Library module 的时候可以将包名修改为发布android包使用的包名,和 unity 中定义的android包名一样即可。这样就可以创建一个 MainActivity 。让最终打出的android包使用的是android端定义的MainActivity。
如果不需要使用自定义的 MainActivity,unity导出生成的 UnityPlayerActivity做为 MainActivity 也满足需求的话,这个library包名就可以不和 unity settings 中的包名保持一致。这种就类似平常接入的sdk,你通过android studio生成的aar也可以认为是一个sdk,加入了自己自定义的一些功能。
在这里插入图片描述
Android Library 类型的 module 创建完成后,工程目录如下图所示。我把module名字修改为了 gamelibrary ,这个名字按喜好随意修改。

下文提到的 gamelibrary 都是这个 module.

在这里插入图片描述
可能遇到的问题:
使用 gradle 可能经常遇到的问题就是因为网络原因无法下载依赖库,这个时候可以修改默认仓库地址为阿里云的仓库地址。
修改工程下的 build.gradle 文件(注意不是module中的build.gradle)。
修改前相关部分代码片段

    repositories {
        google()
        jcenter()
        
    }

修改后相关部分代码片段

    repositories {
        jcenter()
		maven {
            url "https://maven.aliyun.com/repository/jcenter"
        }
        maven {
            url "https://maven.aliyun.com/repository/google"
        }
    }

记得有两处 repositories,都修改下

p.s.其中 jcenter() 仓库大部分是可以使用的,如果修改完同步 gradle 的时候还是报找不到某些库的错误,可以把 jcenter() 也注释掉,只保留aliyun的仓库地址试试。

android端代码

创建android library类型的module后,开始写测试代码。这里创建了一个Activity,其中创建了一个Dialog,这个Dialog通过unity端唤起,同时在点击对话框的confirm按钮后,通过 UnityPlayer.UnitySendMessage 调用unity端c#代码定义的方法。这个方法很简单,其中传递三个参数:

//第一个参数 C# 脚本所挂的 gameobject 名
//第二个参数 C# 脚本类中定义的方法名
//第三个参数 传递的参数
 UnityPlayer.UnitySendMessage("sdk", "CallFromAndroid", "来自android的调用");

UnityPlayer.UnitySendMessage 方法是 unity 官方jar包中提供的方法,所以在使用前需要在module中引入这个jar包,这个jar包位于对应版本unity的安装目录。
…\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes

使用 il2cpp 或者 mono 目录下的 class.jar 都可以,因为最后导出 aar 时不会把 class.jar 打包进去,这里引入 class.jar 只是为了代码不报错,可以正常导出 aar 。

找到 class.jar 拷贝到 gamelibrary module 中 libs 目录中,然后在Android Studio中右键选中 class.jar 执行 Add as Library 就可以把 class.jar 引入到 gamelibrary module中。这个时候打开 gamelibrary 中的 build.gradle 会发现多了一条代码,这个就是执行 Add as Library 操作后 Android Studio 自动帮你处理。如果拷贝完 class.jar 后,自己手动在 build.gradle 中写上这段代码,然后同步下gradle,效果是一样的。

dependencies {
	....
    implementation files('libs/classes.jar')
}

测试 Activity 代码如下:

package com.good.game;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Nullable;

import com.unity3d.player.UnityPlayer;

public class GradleTest extends Activity
{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    /**
     * 调用 unity 中 c# 代码定义的方法
     */
    public static void sendMessageToUnity(){
        UnityPlayer.UnitySendMessage("sdk", "CallFromAndroid", "来自android的调用");
    }

    /**
     * unity端调用这个方法
     */
    public static void showDialog(String msg){
        Log.d("unitycall", "callFromUnity: " + msg);
        final AlertDialog.Builder alertDialog = new AlertDialog.Builder(UnityPlayer.currentActivity).setIcon(R.drawable.app_icon)
                .setTitle("对话框").setMessage(msg)
                .setNegativeButton("close",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        })
                .setPositiveButton("confirm",
                        new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();

                        //关闭对话框的时候调用unity方法
                        sendMessageToUnity();
                    }
                });
        alertDialog.create().show();

    }
}

导出 aar

将上一步编写好代码的 gamelibrary 导出给 unity 使用的时候,不能把引入的 unity jar 包打包进去,因为这次是使用unity出包android,unity会把class.jar再次打到android包中,这样就会出现重复类的错误。所以需要修改 gamelibrary 中的 build.gradle,将 class.jar 修改为只在编译使用。修改后的代码如下:

dependencies {
	//implementation fileTree(include: ['*.jar'], dir: 'libs')
	...
    compileOnly files('libs/classes.jar')
}

同步完毕后,选中 gamelibrary,然后在工具栏找到 Build 接着执行
Build -> Make module ‘gamelibrary’
Make 成功后,会在 build\output\aar 目录生成一个 aar 包。
gamelibrary\build\outputs\aar

unity 中导入 aar

回到 unity ,创建一个工程,然后在工程中新建一个场景,场景中新建一个Text,一个Button和一个空GameObject并将GameObject修改名字为 sdk。再创建两个C#脚本,一个为 Demo.cs 用来实现Button点击调用和Text文本展示字符串。一个 MessageCenter.cs 用来和 java 代码交互。将MessageCenter.cs 拖到 sdk 上。再将Demo.cs拖到 Main Camera 中,然后拖动赋值 LogTxt,ClickButton和MsgCenter。
接下来在 Assets 中创建一个 Plugins 目录,在其中再创建一个 Android 目录,将上一步生成的 gamelibrary-debug.aar 放置到 Android 目录。最终目录如下图所示。
在这里插入图片描述

场景层级如图所示:这个场景可以根据自己需求改变的。
在这里插入图片描述
Demo.cs代码

using UnityEngine;
using UnityEngine.UI;

public class Demo : MonoBehaviour
{

	public Button ClickButton;

	public Text LogTxt;

	public MessageCenter MsgCenter;
	
	// Use this for initialization
	void Start () 
	{
		//android回调后,显示android传递过来的字符串
		MsgCenter.CallBack = str => { LogTxt.text = str; };
		
		ClickButton.onClick.AddListener(OnClickButton);
	}

	/// <summary>
	/// 点击按钮调用android端定义方法
	/// </summary>
	private void OnClickButton()
	{
		Debug.Log("unitycall OnClickButton");
		MsgCenter.CallStaticFunc("showDialog", "来自unity的调用");
	}

}

MessageCenter.cs代码

using System;
using UnityEngine;

public class MessageCenter : MonoBehaviour
{
    public Action<string> CallBack;
    
    /// <summary>
    /// 和android端交互类的完整类名
    /// </summary>
    private const string ClassName = "com.good.game.GradleTest";


    /// <summary>
    /// 调用android端定义静态方法
    /// </summary>
    /// <param name="func">静态方法名</param>
    /// <param name="args">传递参数</param>
    public void CallStaticFunc(string func, params object[] args)
    {
        using (AndroidJavaClass cls = new AndroidJavaClass(ClassName))
        {
            cls.CallStatic(func, args);
        }
    }

    /// <summary>
    /// 调用 android 端定义有返回值静态方法
    /// </summary>
    /// <param name="func">方法名</param>
    /// <param name="args">传递参数</param>
    /// <returns>返回值</returns>
    public int CallStaticFuncValue(string func, params object[] args)
    {
        using (AndroidJavaClass cls = new AndroidJavaClass(ClassName))
        {
            return cls.CallStatic<int>(func, args);
        }
    }

    private void CallFromAndroid(string msg)
    {
        Debug.Log("CallFromAndroid: " + msg);
        if (CallBack != null)
        {
            CallBack.Invoke(msg);
        }
    }

}

这个时候就可以通过unity打android包了。不过这个时候可能会在打包的时候出错。
如上面所说,gamelibrary 可以选择和 unity 使用一样的包名,然后可以创建一个自定义的 MainActivity 覆盖 unity 生成的 UnityPlayerActivity使用。如果是这样的话,在出包的时候有可能会出现
D8: Program type already present: xxxx.BuildConfig 这样的错误。

D8: Program type already present: com.good.game.BuildConfig

这个错误是因为导出的 aar 中有一个 BuildConfig.class,unity出包的时候也会生成一个 BuildConfig.class 这样就会有重复的类,导致出包产生错误。
可以通过解压 aar,然后再解压其中的 class.jar 可以看到这个 BuildConfig.class。将这个 BuildConfig.class 删除后,再生成 aar 即可。具体操作步骤:

  1. 修改 aar 文件扩展名为 zip
  2. 使用 winrar 软件解压上面改名后的 zip
  3. 从解压文件中找到 class.jar 文件,然后使用winrar打开。注意是右键打开,而不是解压。找到 BuildConfig.class 后再右键删除。这步对于 class.jar 的操作都是在 winrar 软件中进行的。
  4. 将第2步解压的所用文件使用 winrar 压缩成 zip 格式文件。注意只需要全选这些文件压缩即可,不要再放到一个单独的文件夹中。
  5. 将上一步的压缩文件修改扩展名为 aar。

有博客说可以通过在 gradle 中配置一个 task ,在生成 aar 的时候剔除这个 BuildConfig.class,我没有试过。其实这个手动删除 BuildConfig.class 在目前的项目中我也没有使用,我是开启了混淆,gradle出包的时候会自动将无用的代码剔除,其中就包括这个 BuildConfig.class。

MainActivity补充:
如果确定要使用自定义的 MainActivity,除了library中使用同样的包名外,还需要修改下 AndroidManifest 文件。需要调整的地方:

  1. 将 MainActivity 定义到 AndroidManifest.xml 中,同时声明为 MainActivity。就是在intent-filter中声明 action 为 MAIN,category 为 LAUNCHER。同时声明 meta-data
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
  1. 将这个 AndroidManifest.xml 文件放置到 unity 工程中的 Plugins/Android 目录下。这样使用 unity 出包时,不会被unity默认的 manifest 覆盖掉。

经过上面的一系列操作,已经实现了Android Studio导出aar给unity,unity选择gradle方式出包。

导出Android Studio 工程

接下来讲第二种方式,通过unity导出Android Studio工程,然后通过Android Studio出包。
和unity出包类似,unity端配置好包名,版本号,targetversion等信息后,在Build Settings中勾选 Export Project ,点击 export 按钮,选择一个目录就可以导出一个 Android Studio 工程。
在这里插入图片描述
然后使用 Android Studio 打开导出的工程,等 gradle 同步完成后,就可以编写java代码了,可以将上一部分的 GradleTest.java 直接拷贝到工程中使用,使用 Android Studio 直接 run 包。运行正常后再使用Build -> Generate Signed Bundle / APK 生成签名的包发布即可。
其实大部分内容还是在编写两头代码上。
这种方式相对上面导出 aar 方式简便了一些,同时不会出现 BuildConfig 问题。

结语

两种方式看项目需求。一般来说android端的java代码不会频繁改动,只是每次升级sdk,如果有api变动或者有其他需求改动,才会进行代码修改。改动后可以保持很长时间。这样一次导出 aar 可以使用很长时间,每次出包使用 unity 发布 APK 即可。
第二种方式因为unity端的代码改动比较频繁,所以基本上每次出包,都会需要先导出Android Studio 工程,再使用Android Studio打包,进行两步操作。当然毕竟使用了gradle,可以通过 OnPostprocessBuild 加 gradle 脚本方式进行脚本打包。像配置了 Jenkins 这种 CI/CD 环境后,可以做到一键出包。
上面的两步手动操作就可以忽略了。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值