跳到主要内容

集成 SDK

集成须知​

警告
  1. 新项目请直接使用 API v3
  2. OmniSDK 本身不会申请任何权限,权限一律由游戏或渠道 SDK 申请。
  3. OmniSDK 最低兼容 Android 版本为 Android 5.0(API Level 21)。
  4. Unity 工程建议将和 OmniSDK 交互的代码放入到 launcher 模块

开始集成​

导出中间工程​

  1. 根据游戏引擎文档 导出 Gradle 工程结构 章节,导出 Android 工程结构(选择 AndroidX,如果有)。
  2. OmniSDK 目前最低支持 AGP 3.4.3 & Gradle 5.3.1,请确认引擎导出的 Gradle 版本号。
  3. 使用 Android Studio 打开导出的中间工程,根路径要有 settings.gradle 文件。
  4. 检查根路径下 gradle.properties 文件内 AndroidX 配置是否开启,如果没有则添加:
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true

GUI 工具配置​

根据 GUI 工具指南 初始化 Android 工程配置。

提示

GUI 工具首次是必须使用的,需要通过 GUI 工具初始化 Android 工程所依赖的配置,后续可以通过命令行进行指令操作

Gradle 配置​

警告
  • 注意高亮内容,同时注意写入顺序。
  • rootProject.rootDir 直接复制,不需要替换

注册 classpath​

在 root-level (根目录) 的 build.gradle,添加如下配置

buildscript {
repositories {
mavenCentral()
google()

// OmniSDK 仓库
apply from: ("${rootProject.rootDir}/kssyOmniBuildscriptRepositories.gradle")
// 如果有用到类似阿里的镜像库,需要放在最后
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'

// OmniSDK 相关的 classpath
apply from: ("${rootProject.rootDir}/kssyOmniBuildscriptDependencies.gradle")
}
}

allprojects {
repositories {
mavenCentral()
google()

// 如果有用到类似阿里的镜像库,需要放在最后
}
}

// OmniSDK 渠道仓库
apply from: ("${rootProject.rootDir}/kssyOmniChannelsRoot.gradle")

声明 OmniSDK 依赖​

提示

app-level: Unity 导出 Android 工程的 launcher 目录

在 app-level 的 build.gradle (注册了 apply plugin: 'com.android.application' ),添加如下配置:

apply plugin: 'com.android.application' 

// 声明 KSSYOmniPlugin
apply from: ("${rootProject.rootDir}/kssyOmniPlugin.gradle")

// 声明 OmniSDK 依赖
apply from: ("${rootProject.rootDir}/kssyOmniChannelsApp.gradle")

// 在需要使用 OmniSDK API 代码的 Module 声明kssyOmniChannels.gradle。
// 声明 渠道 依赖
apply from: ("${rootProject.rootDir}/kssyOmniChannels.gradle")

// 签名配置,为保证安全,密钥密码不要明文配置在这里,而是通过文件读取方式配置。比如 signing.properties
def signingPro = new Properties()
signingPro.load(new FileInputStream(file(rootProject.file("signing.properties"))))

// 签名变量,注意这里加 ext. 以便全局可以引用到
ext.signing_keystoreFile = file("${rootProject.rootDir}/${signingPro["storeFilePath"]}")
ext.signing_keystorePassword = signingPro["storePassword"]
ext.signing_keyAlias = signingPro["keyAlias"]
ext.signing_keyPassword = signingPro["keyPassword"]

android {
compileSdkVersion 31 // 这里因部分Android官方依赖库要求最低 31
// buildToolsVersion 如果引擎导出有此配置请直接注释删除,由 compileSdkVersion 控制

defaultConfig {
// applicationId "游戏包名" // 本行删除,不要自己配置,编译脚本会自动读取 project_config.json#package_name。
// 包名必须 project_config.json#package_name、OmniSDK后台、相关第三方SDK后台,保持一致;
// 变更包名时需要同步所有地方的包名及相应申请的参数。
minSdkVersion 21 // 最低版本仅支持21
targetSdkVersion 28 // 国内最低28,海外最低31,渠道指定版本由OmniSDK编译插件处理。

versionCode 1
versionName "1.0"

ndk {
abiFilters "arm64-v8a" // 64位适配,扩展阅读:https://docs.seayoo.com/sdk/package/android/build-gui#arm64-config
}
}

// 开启 Java8 兼容性编译(必选)(不得低于8,除非游戏引擎要求更高版本)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

signingConfigs {
release {
storeFile signing_keystoreFile
storePassword signing_keystorePassword
keyAlias signing_keyAlias
keyPassword signing_keyPassword
}
}

buildTypes {
release {
minifyEnabled true // 开启代码混淆:保护代码、减少包大小
// multiDexKeepProguard = file("multidex-config.pro") // 分包配置,如果需要
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release // release签名(引用kssyOmniSigning.gradle时需要注释掉)
}
}
}

dependencies {
implementation(project(":library"))
}

在需要使用 OmniSDK API 的模块内可以声明 kssyOmniChannels.gradle。

apply plugin: 'com.android.library' 

// 在需要使用 OmniSDK API 代码的 Module 声明kssyOmniChannels.gradle。
// 声明 渠道 依赖
apply from: ("${rootProject.rootDir}/kssyOmniChannels.gradle")

同步依赖【同步Gradle依赖-的几种操作方法】

签名配置​

  1. 查看文件 kssyOmniSigning.gradle,如果有接入需要 v1 签名渠道,则按下面的方法配置处理。
  2. 即没有接入 v1 签名渠道,也可以按下面的方法处理。
  3. 这样出包时,自动根据渠道要求使用对应签名版本,不需要再手动签名。
  • 在 app-level 的 build.gradle (注册了 apply plugin: 'com.android.application' ),添加如下配置:
// 要完成上面步骤的配置后

android {
buildTypes {
release {
...
// signingConfig signingConfigs.release // 注释掉这行,其他不变,签名版本由 kssyOmniSigning.gradle 配置控制
}
}
}

// 注意这行的顺序,是在 android{} 下方声明
apply from: ("${rootProject.rootDir}/kssyOmniSigning.gradle")

Application 接入​

OmniSDK 支持两种方式接入 Application。

  • 游戏侧没有自定义的 Application,则需要创建 GameApplication.java 并继承 OmniApplication
  • 游戏侧有自定义的 Application,但继承的基类是 android.app.Application,或者游戏接入的渠道包含特殊渠道,可将继承的基类直接改为 OmniApplication
import com.kingsoft.shiyou.omnisdk.api.OmniApplication;

public class GameApplication extends OmniApplication {
@Override
public void attachBaseContext(Context base) {
super.attachBaseContext(base);
......
}
}

如果游戏的 Application 继承了其它 SDK 的 Application 类,则不能直接继承 OmniApplication,需要在其中添加如下代码:

Show Code
@Override
public void attachBaseContext(Context context) {
super.attachBaseContext(context);
MultiDex.install(context); // 64k方法数
OmniSDKv3.getInstance().onApplicationAttachBaseContext(context);
}

@Override
public void onCreate() {
super.onCreate();
OmniSDKv3.getInstance().onApplicationCreate(context);
}

@Override
public void onLowMemory() {
super.onLowMemory();
OmniSDKv3.getInstance().onApplicationLowMemory();
}

@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
OmniSDKv3.getInstance().onApplicationTrimMemory();
}

@Override
public void onTerminate() {
super.onTerminate();
OmniSDKv3.getInstance().onApplicationTerminate();
}

特殊渠道列表​

渠道名渠道唯一标识
魅拓meituo
海南澎湃互动pphd
多酷duoku
MuMuyofun
游戏狗gamedog
汇智huizhi
熊猫侠xmxia

AndroidManifest​

提示
  • android:name 需要替换为游戏的 Application (如 GameApplication)
  • android:icon="@mipmap/omni_ic_launcher" 不同的渠道会有不同的 icon,所以设置为 omni_ic_launcher 作为动态替换的标记位
  • 添加 tools:replace 元素、并为其设置属性
<application
android:name="[YOUR_PACKAGE_PATH].GameApplication"
android:icon="@mipmap/omni_ic_launcher"
android:label="@string/app_name"
tools:replace="android:icon,android:label,android:theme,android:allowBackup">
...

<activity
android:name="[YOUR_PACKAGE_PATH].GameActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:exported="true"
android:screenOrientation="sensor"
android:theme="@style/Theme.GameActivity.Fullscreen">

<intent-filter>
<!-- 主Activity即启动Activity,需要至少声明这两个属性 -->
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

GameActivity 接入​

提示

如果游戏侧有依赖第三方的 SDK,同时也已经过修改 Activity,推荐使用 方式二

需要修改 Android 工程的 Activity,一般是由游戏引擎生成 GameActivity 或 UnityPlayerActivity,需要手动修改将继承的基类变更为 OmniSdkActivity

public class GameActivity extends OmniSdkActivity {}

或

public class UnityPlayerActivity extends OmniSdkActivity {}

游戏侧已经创建好了 Activity,那只需要在该类中添加 OmniSDK 的生命周期方法,即 OmniSdkActivity 里所有的方法

Show Code
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;

import com.unity3d.player.UnityPlayerActivity;
import com.kingsoft.shiyou.omnisdk.api.OmniApplication;

public class GameActivity extends UnityPlayerActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OmniSDKv3.getInstance().onCreate(this, savedInstanceState);
}

@Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
OmniSDKv3.getInstance().onNewIntent(this, intent);
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
OmniSDKv3.getInstance().onSaveInstanceState(this, outState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
OmniSDKv3.getInstance().onRestoreInstanceState(this, savedInstanceState);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
OmniSDKv3.getInstance().onActivityResult(this, requestCode, resultCode, data);
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
OmniSDKv3.getInstance().onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}

@Override
public void onBackPressed() {
super.onBackPressed();
OmniSDKv3.getInstance().onBackPressed(this);
}

@Override
protected void onDestroy ()
{
super.onDestroy();
OmniSDKv3.getInstance().onDestroy(this);
}

@Override
protected void onStart()
{
super.onStart();
OmniSDKv3.getInstance().onStart(this);
}

@Override
protected void onPause()
{
super.onPause();
OmniSDKv3.getInstance().onPause(this);
}

@Override
protected void onResume()
{
super.onResume();
OmniSDKv3.getInstance().onResume(this);
}

@Override
protected void onRestart() {
super.onStart();
OmniSDKv3.getInstance().onRestart(this);
}

@Override
protected void onStop() {
super.onStop();
OmniSDKv3.getInstance().onStop(this);
}

@Override
public void onLowMemory()
{
super.onLowMemory();
}

@Override
public void onTrimMemory(int level)
{
super.onTrimMemory(level);
}

@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
OmniSDKv3.getInstance().onConfigurationChanged(this, newConfig);
}

@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
}

@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
return super.dispatchKeyEvent(event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
return super.onKeyUp(keyCode, event);
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (OmniSDKv3.getInstance().onKeyDown(this, keyCode, event)) {
return true;
} else {
return super.onKeyDown(
keyCode,
event
);
}
}

@Override
public boolean onTouchEvent(MotionEvent event)
{
return super.onTouchEvent(event);
}
}

混淆配置​

混淆说明​

OmniSDK 及各渠道的混淆配置,集成在依赖包内,游戏无需额外配置。

64k 方法数分包需要游戏自身配置,请参考multidex-config.pro 下载

开启行号​

开启 proguard-rules.pro 行号配置,方便准确定位问题代码。

# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFile

游戏运行时混淆问题​

如果 Debug 包 没有问题,Release 包 编译无问题,但是运行时有问题,通常是混淆原因。

如果游戏有接入其他第三方 SDK,请根据其接入文档增加对应的混淆配置。

线程​

提示
  • OmniSDK 对线程的调用无要求,游戏调用接口时无需开启新线程
  • 回调时,无需额外切换线程

自动化​

Unity​

可以使用 Unity 的 Post Build 机制去 Setup Android 工程,可以避免因更换机器或者清理 Workspace 导致需要重新配置

警告

当 OmniSDK 有 BREAKING CHANGES,可能需要通过 GUI 重新初始化配置

Step 1

需要通过 GUI 工具 完成工程的初始化,会获取如下配置文件

img

Step 2

可以将初始化完成后的文件放置于 Unity 项目的某个位置,通过 Unity Post Build 机制可以将文件拷贝到 Android Workspace

Examples

示例: 文件存放在 NativeAssets/Android 目录内

img

AndroidPostBuildProcessor.cs

class AndroidPostBuildProcessor : IPostGenerateGradleAndroidProject
{
public int callbackOrder { get { return 0; } }

public void OnPostGenerateGradleAndroidProject(string path)
{
var rootPath = GetRootPath();

var nativeAssetsPath = Path.Combine(rootPath, "NativeAssets", "Android");
Debug.Log(nativeAssetsPath);

var targetPath = GetOutputPath(path);

CopyDirectory(nativeAssetsPath, targetPath);
}

private static string GetRootPath()
{
string projectRootPath = Path.GetFullPath(Application.dataPath);
return Path.GetDirectoryName(projectRootPath);
}

private static string GetOutputPath(string path)
{
return Path.GetDirectoryName(path);
}

public static void CopyDirectory(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);

CopyAll(diSource, diTarget);
}

public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
Directory.CreateDirectory(target.FullName);

// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
{
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
}

// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}
}

Step 3

后续可通过命令行 自助出包