unity-Android库开发工作流

目的是用最少的操作, 保证工作流的正确性.


前篇

首先要明白的是, unity 中使用到的 Android jar 包 是以 第三方库 的形式打包到 apk 中.


开发工作流

这种工作流使用的是 unity 直接打包 apk/aab, 而不是导出一个 as 项目工程, 在用 as 去打包.

  1. 新建一个 as 的 Android 项目, 创建两个模块

    1. application 模块.

      使用 android-Androidd单元测试-Espresso, 快速测试 library 模块中的接口. 或者 快速打包成 apk 到 模拟器 上测试. 这种方式测试接口的效率远高于使用 unity 打包出来 apk 测试.

    2. library 模块

      unity 中使用的各种 Android 接口逻辑代码.

  2. 在 as 中编写自动化导出 jar 的 build.gradle 逻辑. 然后在 gradle 窗口直接双击一下即可. 脚本参照这里: [build.gradle 导出 jar 脚本](#build.gradle 导出 jar 脚本)

    1. library 模块 build 出来的是个 aar 包, 把它拷贝到一个临时目录
    2. 用 zip 解压 aar, 拿到 classes.jar, 拷贝到 unity 的平台目录下 Assets/Plugins/Android, 随便重命名一下
  3. 将 Android 平台使用到的 动态参数 统一配置到 gradle 中, unity 中是 Assets/Plugins/Android/mainTemplate.gradle (官方提供的模板).

    统一起来方便管理

    比如配置渠道参数等等, 有几种方式可以在代码中动态获取到这些参数

    1. AndroidManifest.xml 中配置 application,activity,service 中的 meta 参数. 而 AndroidManifest.xml 又可以在 gradle 中配置.

    2. BuildConfig 中配置.

    3. res 中的 xml 中配置.

      代码看起来差不多这样

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      android {
      defaultConfig {
      // ------ its 变量定义 begin ------
      // google
      resValue('string', 'google_login_app_id', 'aaa') // 配置 res 中的 xml 键值对

      // facebook
      def fb_appid = "aaa"
      resValue('string', 'facebook_app_id', fb_appid)
      resValue('string', 'fb_login_protocol_scheme', "fb${fb_appid}")

      // firebase
      resValue('string', 'default_web_client_id', 'aaa')
      resValue('string', 'firebase_database_url', 'aaa')

      manifestPlaceholders = [ // 替换 AndroidManifest.xml 的占位符
      ps_appid : "${android.defaultConfig.applicationId}",
      ps_fbappid : "${fb_appid}"
      ]
      // ------ its 变量定义 end ------
      }
      }

多渠道管理

建多个渠道的 Android 相关东西分别丢到对应的目录中, 这里 Channel 是在 unity 项目根目录下.

然后在自定义打包代码中, 打包前 把对应渠道的 Android 目录拷贝到 Assets\Plugins 目录下, 就就可以打包该渠道的 apk.


build.gradle 导出 jar 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def tempOutputDir = "a_aarOutputs" // 临时存放目录
def tempZipName = "app.zip"
def dstJarName = "AndroidLib.jar"
def MyTaskGroup = "a_mytask"

task a_DelTempDir(type: Delete) {
group = "a_mytask"
delete(tempOutputDir)
}

task a_ItsCopyAAR(type: Copy) {
group = "a_mytask"
from('build/outputs/aar/app-debug.aar')
into(tempOutputDir)
rename('app-debug.aar', tempZipName)
}

task a_unzip(type: Copy) {
group = "a_mytask"
def zipFile = file("${tempOutputDir}/app.zip")
def outputDir = file(tempOutputDir)

from zipTree(zipFile)
into outputDir
}

task a_ItsCopyJar(type: Copy) {
group = "a_mytask"
def srcPath = "${tempOutputDir}/classes.jar"
def dstPath = "../../Assets/Plugins/Android"
from(srcPath)
into(dstPath)
rename('classes.jar', dstJarName)
}

a_ItsCopyAAR.dependsOn(a_DelTempDir, build)
a_unzip.dependsOn(a_ItsCopyAAR)
a_ItsCopyJar.dependsOn(a_unzip)

// 去掉 资源校验 任务, 不然会 build 失败
tasks.whenTaskAdded { task ->
if (task.name.contains("verifyReleaseResources")) {
task.enabled = false
}
}