- 1 安卓相关
- 1.0 Android打包
- 1.1 Unity Android打包原理
- 1.2 注意事项
- 1.3 关于原生代码修改
- 2 IOS相关
- 2.1 打包流程
- 2.2 AppController.mm模板
- 2.3 XCode库引用
- 3 自动化构建
- 4 其他
需要jdk,sdk,大概是1.7~1.8的jdk太高的版本可能不行。
1.1 Unity Android打包原理Unity大概是构建了一个只有一个MainActivity的原生安卓,然后将C#的代码跨平台转换成java的UnityPlayer类,在Unity工程Plugins/Android下的库文件则会成为安卓工程的库文件。
1.2 注意事项/// 1 AndroidManifest.xml,Unity的Bundle Identifier,SDK开发者申请的推送包名。3个包名必须一致
/// 2 AndroidManifest中不仅更换package,同名的一些属性都要替换
/// 3 AndroidManifest更换AppKey
/// 4 jPush.aar必须在Assets/Plugins/Android下,其他目录不被识别
/// 5 多个SDK中的AndroidManifest的权限需要合并
aar文件是安卓导出的带有资源的模块,想要修改原生代码可以在aar对应的工程下修改,然后重新导出aar。
2 IOS相关 2.1 打包流程同安卓,要将库文件丢到Plugins/iOS路径下,不可修改路径。
在mac上打包完成后得到一份xcode工程,需要额外导入一些库以及修改AppController.mm。
然后再xcode上编译出ipa即可。证书相关不做介绍。
Unity提供了模板机制可以在打包完成后的回调中自动修改AppController.mm,免去每次打包都要修改参数的问题。
其中类名必须为XXXAppController.mm格式
// ****************************************************************** // / /| @file WarGameAppController.mm // V/ @brief 自定义IOS AppController // | "") @author Shadowrabbit, yingtu0401@gmail.com // / | // / \ @Modified 2021-11-04 06:54:35 // *(___ @Copyright Copyright (c) 2021, Shadowrabbit // ****************************************************************** //可以在编译后的xcode工程里插入一些代码 //极光推送start #import "JPUSHService.h" #import "JPushEventCache.h" #import2.3 XCode库引用//#import //如需使用广告标识符 IDFA 则添加该头文件,否则不添加。 //极光推送end #import "UnityAppController.h" @interface WarGameAppController : UnityAppController @end IMPL_APP_CONTROLLER_SUBCLASS (WarGameAppController) //告知Unity使用我们自定义的Controller @implementation WarGameAppController - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[JPushEventCache sharedInstance] handFinishLaunchOption:launchOptions]; [JPUSHService setupWithOption:launchOptions appKey:@"[JPushAppKey]" channel:@"" apsForProduction:NO]; // NSString *advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; // [JPUSHService setupWithOption:launchOptions appKey:@"替换成你自己的 Appkey" channel:@"" apsForProduction:NO SadvertisingIdentifier:advertisingId]; [super application:application didFinishLaunchingWithOptions:launchOptions]; return YES; } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // Required. [JPUSHService registerDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { // Required. [[JPushEventCache sharedInstance] sendEvent:userInfo withKey:@"JPushPluginReceiveNotification"]; [JPUSHService handleRemoteNotification:userInfo]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler { [[JPushEventCache sharedInstance] sendEvent:userInfo withKey:@"JPushPluginReceiveNotification"]; } @end
Unity同样提供了回调函数可供我们在打包完成后向XCode工程添加库依赖。
[PostProcessBuild]
public static void onPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = PBXProject.GetPBXProjectPath(path);
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.GetUnityframeworkTargetGuid();
//极光推送相关start
proj.AddframeworkToProject(target, "CFNetwork.framework", false);
proj.AddframeworkToProject(target, "CoreFoundation.framework", false);
proj.AddframeworkToProject(target, "CoreTelephony.framework", false);
//proj.AddframeworkToProject(target, "SystemConfiguration.framework", false);
proj.AddframeworkToProject(target, "CoreGraphics.framework", false);
proj.AddframeworkToProject(target, "Foundation.framework", false);
proj.AddframeworkToProject(target, "UIKit.framework", false);
proj.AddframeworkToProject(target, "Security.framework", false);
proj.AddframeworkToProject(target, "libz.tbd", false); //(Xcode 7 以下版本是 libz.dylib)
proj.AddframeworkToProject(target, "AdSupport.framework", false);
proj.AddframeworkToProject(target, "UserNotifications.framework", false);
proj.AddframeworkToProject(target, "libresolv.tbd",
false); //(JPush 2.2.0 及以上版本需要,Xcode 7 以下版本是 libresolv.dylib)
proj.AddframeworkToProject(target, "WebKit.framework", false);
//极光推送相关end
//拷贝添加第三方framework
proj.AddBuildProperty(target, "frameWORK_SEARCH_PATHS", "$(PROJECT_DIR)/frameworks");
proj.SetBuildProperty(target, "USYM_UPLOAD_AUTH_TOKEN", "0000-0000-0000-0000-000000");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(target, "MinimumOSVersion", "9.0");
string main = proj.GetUnityMainTargetGuid();
proj.SetBuildProperty(main, "USYM_UPLOAD_AUTH_TOKEN", "0000-0000-0000-0000-000000");
proj.SetBuildProperty(main, "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(main, "MinimumOSVersion", "9.0");
//保存project
File.WriteAllText(projPath, proj.WriteToString());
//修改Plist
string plistPath = path + "/Info.plist";
Plistdocument plist = new Plistdocument();
plist.ReadFromString(File.ReadAllText(plistPath));
PlistElementDict rootDict = plist.root;
rootDict.SetString("NSMicrophoneUsageDescription", "麦克风");
rootDict.SetBoolean("UIFileSharingEnabled", true); //设置Application.persistentDataPath目录共享,可以通过itune打开
rootDict.SetBoolean("FirebaseAppStoreReceiptURLCheckEnabled", false);
//保存plist
File.WriteAllText(plistPath, plist.WriteToString());
}
}
3 自动化构建
我们可以在打包前进行一些SDK相关的参数处理,比如把AppKey配置到表格里动态获取,免得频繁更换带来繁琐工作量。
Manifest清单文件中的包名更换和AppKey更换。AppController模板中的AppKey更换。可以配合Jenkins从Jenkins获取一部分参数,其余的根据表格获取。
private const string DefaultAndroidPackageName = "xx.xx.xx"; //安卓默认包名 private const string DefaultIosPackageName = "xx.xx.xx"; //IOS默认包名 private const string AppKeyNameKey = "[JPushAppKey]"; //WarGameAppController.mm 与 Manifest的appKey占位符 private const string PackageNameKeyInJenkins = "BundleIdentifier"; //Jenkins上的包名的键 private const string PackageNameKey = "[PackageName]"; //安卓Manifest上的包名占位符 ///4 其他/// 极光SDK相关参数修改 /// private static void SetAndroidJPushSetting() { //安卓清单文件包名替换 AppKey替换 var packageName = GameUtility.GetEnvString(PackageNameKeyInJenkins); if (string.IsNullOrEmpty(packageName)) { packageName = DefaultAndroidPackageName; CommonLog.Error(MAuthor.WY2, $"从Jenkins没有获取到包名,使用默认:{DefaultAndroidPackageName}"); } if (!CfgSdkJPush.TryGetCfg(packageName, out var cfg)) { CommonLog.Error(MAuthor.WY2, $"没有找到包名:{packageName}的极光推送配置!packageNameKeyInJenkins:{PackageNameKeyInJenkins}"); return; } var appKey = cfg.JPushAppKey; try { var readFileSteam = new FileStream(ManifestPath, FileMode.Open, FileAccess.Read); var steamReader = new StreamReader(readFileSteam); var con = steamReader.ReadToEnd(); con = con.Replace(PackageNameKey, packageName); con = con.Replace(AppKeyNameKey, appKey); steamReader.Close(); readFileSteam.Close(); var writeFileSteam = new FileStream(ManifestPath, FileMode.Open, FileAccess.Write); var steamWriter = new StreamWriter(writeFileSteam); steamWriter.WriteLine(con); steamWriter.Close(); writeFileSteam.Close(); } catch (Exception e) { CommonLog.Error(MAuthor.WY2, e.ToString()); throw; } } ////// 极光SDK相关参数修改 /// private static void SetIosJPushSetting() { //IOS 模板AppKey替换 var packageName = GameUtility.GetEnvString(PackageNameKeyInJenkins); if (string.IsNullOrEmpty(packageName)) { packageName = DefaultIosPackageName; CommonLog.Error(MAuthor.WY2, $"从Jenkins没有获取到包名,使用默认:{DefaultIosPackageName}"); } if (!CfgSdkJPush.TryGetCfg(packageName, out var cfg)) { CommonLog.Error(MAuthor.WY2, $"没有找到包名:{packageName}的极光推送配置!packageNameKeyInJenkins:{PackageNameKeyInJenkins}"); return; } var appKey = cfg.JPushAppKey; try { var readFileSteam = new FileStream(AppControllerPath, FileMode.Open, FileAccess.Read); var steamReader = new StreamReader(readFileSteam); var con = steamReader.ReadToEnd(); con = con.Replace(AppKeyNameKey, appKey); steamReader.Close(); readFileSteam.Close(); var writeFileSteam = new FileStream(AppControllerPath, FileMode.Open, FileAccess.Write); var steamWriter = new StreamWriter(writeFileSteam); steamWriter.WriteLine(con); steamWriter.Close(); writeFileSteam.Close(); } catch (Exception e) { CommonLog.Error(MAuthor.WY2, e.ToString()); throw; } } ////// 加载表格 /// private static void LoadXlsx() { //参数设置回调触发时,表格还没有初始化 手动加载表格 CfgSdkJPush.GetAllCfg().Clear(); ConfigLoader.loadDir = Path.GetFullPath(Application.dataPath + "/../../warconfig/_csv_client"); CfgSdkJPush.Load(); }
推送是依赖进程长链接实现的,理论上游戏进程被杀死就不会再收到推送了,但SDK可以做到依靠其他接入SDK的应用来传递消息,比如一直开着QQ,其他的应用推送消息可以走QQ的通道推送给用户。
还有就是系统级的厂商SDK推送,要接入小米、华为、oppo、vivo之类的厂商sdk,每个都要注册企业账号,比较麻烦。



