集成 SDK
集成须知
- 新项目请直接使用 API v3
- OmniSDK 本身不会申请任何权限,权限一律由游戏或渠道 SDK 申请。
- OmniSDK 最低兼容 Android 版本为
Android 5.0(API Level 21)
。 - Unity 工程建议将和 OmniSDK 交互的代码放入到 launcher 模块
开始集成
导出中间工程
- 根据游戏引擎文档
导出 Gradle 工程结构
章节,导出 Android 工程结构(选择 AndroidX,如果有)。 - OmniSDK 目前最低支持 AGP 3.4.3 & Gradle 5.3.1,请确认引擎导出的 Gradle 版本号。
- 使用 Android Studio 打开导出的中间工程,根路径要有
settings.gradle
文件。 - 检查根路径下
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")
签名配置
- 查看文件
kssyOmniSigning.gradle
,如果有接入需要 v1 签名渠道,则按下面的方法配置处理。 - 即没有接入 v1 签名渠道,也可以按下面的方法处理。
- 这样出包时,自动根据渠道要求使用对应签名版本,不需要再手动签名。
- 在
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 |
MuMu | yofun |
游戏狗 | 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 工具 完成工程的初始化,会获取如下配置文件
Step 2
可以将初始化完成后的文件放置于 Unity 项目的某个位置,通过 Unity Post Build 机制可以将文件拷贝到 Android Workspace
Examples
示例: 文件存放在 NativeAssets/Android
目录内
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
后续可通过命令行 自助出包