TopOn 广告聚合 Unity SDK 分析

介绍

TopOn 是一站式广告聚合平台。

TopOn 提供多种平台 SDK 集成,支持 Android、iOS、Unity、Cocos2dx、CocosCreator。这里分析一下 Unity SDK 的实现原理,主要说明 Unity 与 Native 如何通信、框架文件结构。

环境

  • TopOn SDK v5.8.13
  • Unity 2019.4.32f1

下载

SDK

  • Android v5.8.13 2022-03-11 TopOn SDK(编译后0.61MB)+全部广告平台SDK 39.82MB(编译后19.26MB)
  • iOS v5.8.13 2022-03-11 TopOn SDK(编译后0.99MB)+全部广告平台SDK 603.27MB(编译后29.68MB)

下载方法

选择所有广告平台,然后点击 Integrate 后下载。

实现原理

TopOn 将广告平台的 SDK 封装打包成 unitypackage 文件,然后根据勾选将这些 unitypackage 打包成一个 zip 文件提供下载。

SDK Demo

克隆完仓库需要手动切换到 v5.8.13 分支

文档

通信

使用 RPC 进行 Unity 与 Android/iOS 之间的信息交互。

Unity to Android

原理

Instances of UnityEngine.AndroidJavaObject and UnityEngine.AndroidJavaClass have a one-to-one mapping to an instance of java.lang.Object and java.lang.Class (or their subclasses) on the Java side, respectively. They essentially provide 3 types of interaction with the Java side:

  • Call a method
  • Get the value of a field
  • Set the value of a field

C# 调用 Java 代码使用的是 AndroidJavaObject 来调用 Java 方法。

代码

C# Assets/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs

1 2 3 4 
AnyThinkAds.Android.ATSDKAPIClient.initSDK this.sdkInitHelper = new AndroidJavaObject("com.anythink.unitybridge.sdkinit.SDKInitHelper", this); this.sdkInitHelper.Call("initAppliction", appId, appKey); 

Java Assets/AnyThinkAds/Plugins/Android/anythink_bridge.aar

1 
com.anythink.unitybridge.sdkinit.SDKInitHelper.initAppliction(final String appid, String appkey) 

Android to Unity

原理

Java 层使用构造方法注入 C# 的回调方法

代码

Java Assets/AnyThinkAds/Plugins/Android/anythink_bridge.aar

1 2 3 4 5 6 
public SDKInitHelper(SDKInitListener pSDKInitListener) public interface SDKInitListener { void initSDKSuccess(String str); void initSDKError(String str, String str2); } 

C# Assets/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs

 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 
this.sdkInitHelper = new AndroidJavaObject("com.anythink.unitybridge.sdkinit.SDKInitHelper", this); public void initSDKSuccess(string appid) { Debug.Log("initSDKSuccess...unity3d."); if(sdkInitListener != null){ sdkInitListener.initSuccess(); } } public void initSDKError(string appid, string message) { Debug.Log("initSDKError..unity3d.."); if (sdkInitListener != null) { sdkInitListener.initFail(message); } } 

Unity to iOS

原理

使用 C 接口调用 Native 代码

1 2 3 
extern "C" { float FooPluginFunction(); } 

代码

C# Assets/AnyThinkAds/Platform/iOS/Internal/Script/ATUnityCBridge.cs

 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 
ATUnityCBridge.SendMessageToC("ATUnityManager", "startSDKWithAppID:appKey:", new object[]{appID, appKey}); static public bool SendMessageToC(string className, string selector, object[] arguments, bool carryCallback) { Debug.Log("Unity: ATUnityCBridge::SendMessageToC()"); Dictionary<string, object> msgDict = new Dictionary<string, object>(); msgDict.Add("class", className); msgDict.Add("selector", selector); msgDict.Add("arguments", arguments); CCallBack callback = null; if (carryCallback) callback = MessageFromC; #if UNITY_IOS || UNITY_IPHONE  return message_from_unity(JsonMapper.ToJson(msgDict), callback); #else  return false; #endif } #if UNITY_IOS || UNITY_IPHONE [DllImport("__Internal")] extern static bool message_from_unity(string msg, Func<string, int> callback); #endif 

Objective-C Assets/AnyThinkAds/Platform/iOS/Internal/C/ATUnityManager.m

这里通过反射获取要调用的类名,然后再将调用分发到这个类上进行处理。并且这里存储了后续需要使用的回调方法。

 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 
/*  *class:  *selector:  *arguments:  */ bool message_from_unity(const char *msg, void(*callback)(const char*, const char *)) { NSString *msgStr = [NSString stringWithUTF8String:msg]; NSDictionary *msgDict = [NSJSONSerialization JSONObjectWithData:[msgStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; Class class = NSClassFromString(msgDict[@"class"]); bool ret = false; ret = [[[class sharedInstance] selWrapperClassWithDict:msgDict callback:callback != NULL ? callback : nil] boolValue]; return ret; } 

iOS to Unity

原理

Calling C# back from native code

Unity iOS supports limited native-to-managed callback functionality. You can do this in one of two ways:

  • Using UnitySendMessage
  • Via delegates

代码

TopOn 这里实现得比较复杂,但是核心原理简单。通过将 C# 的回调方法注入到 Objective-C 中,并且将回调地址存储到 placementId 对应的 Value 字典中,后续回调时通过 placementId 查找到回调再调用。

Objective-C Assets/AnyThinkAds/Platform/iOS/Internal/C/ATBaseUnityWrapper.m

 1  2  3  4  5  6  7  8  9 10 11 12 
-(void) invokeCallback:(NSString*)callback placementID:(NSString*)placementID error:(NSError*)error extra:(NSDictionary*)extra { if ([self callbackForKey:placementID] != NULL) { if ([callback isKindOfClass:[NSString class]] && [callback length] > 0) { NSMutableDictionary *paraDict = [NSMutableDictionary dictionaryWithObject:callback forKey:@"callback"]; ... [self callbackForKey:placementID]([self scriptWrapperClass].UTF8String, paraDict.jsonString.UTF8String); } } } 

C# Assets/AnyThinkAds/Platform/iOS/Internal/Script/ATUnityCBridge.cs

 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 
[MonoPInvokeCallback(typeof(CCallBack))] static public void MessageFromC(string wrapperClass, string msg) { Debug.Log("Unity: ATUnityCBridge::MessageFromC(" + wrapperClass + "," + msg + ")"); JsonData jsonData = JsonMapper.ToObject(msg); if (wrapperClass.Equals("ATRewardedVideoWrapper")) { Debug.Log("Unity: ATUnityCBridge::MessageFromC(), hit rv"); ATRewardedVideoWrapper.InvokeCallback(jsonData); } else if (wrapperClass.Equals("ATNativeAdWrapper")) { ATNativeAdWrapper.InvokeCallback(jsonData); } else if (wrapperClass.Equals("ATInterstitialAdWrapper")) { ATInterstitialAdWrapper.InvokeCallback(jsonData); } else if (wrapperClass.Equals("ATBannerAdWrapper")) { ATBannerAdWrapper.InvokeCallback(jsonData); } else if (wrapperClass.Equals("ATNativeBannerAdWrapper")) { ATNativeBannerAdWrapper.InvokeCallback(jsonData); } } 

比较

TopOn SDK 将 Unity 与 Android 通信实现得较为简单,将 Unity 与 iOS 通信实现得较为复杂。

Demo

Demo 都是以最简的代码实现了一个例子,没有任何多余功能。

Android

Android Demo 是一个 Gradle 工程,同时附带了已经编译好可运行的 apk,使用模拟器可以直接查看效果。

iOS

iOS Demo 是一个 Xcode 工程,使用 GitHub - CocoaPods/CocoaPods: The Cocoa Dependency Manager. 对库进行管理,所以仓库里并没有库文件。

总结

  1. 实现了一层薄薄的胶水层,提供 Unity C# 方便逻辑代码调用,同时定义回调方便发生事件时通知。
  2. 内部使用工厂模式创建对应平台对象,使用接口多态动态分发请求到不同平台的实现(Android、iOS、Unity 编辑器)。
  3. 逻辑代码全在 Native 层(Android、iOS),并且做了混淆与加密。

Android

  1. 所有的代码都按照功能分别编译到单独的 aar 文件中。
  2. 针对每一个广告 SDK 都单独编写了 Native 的桥接代码,如:anythink_network_unity_baidu.aar
  3. 广告 SDK 原始库文件 aar 与桥接 aar 放在一起使用。
  4. aar 反编译可以看到所有的 Java 代码都已经进行了混淆。

SDK 核心代码

1 2 3 4 5 6 7 8 9 
anythink_bridge.aar anythink_banner.aar anythink_china_core.aar anythink_core.aar anythink_interstitial.aar anythink_native.aar anythink_rewardvideo.aar anythink_splash.aar tramini_sdk.aar 

iOS

  1. 所有的代码都按照功能分别编译到单独的 Framework 文件中。
  2. 针对每一个 SDK 都单独编写了 Native 的桥接代码,如:AnyThinkBaiduAdapter.framework
  3. 桥接代码与 SDK 代码直接静态编译到了一起。

SDK 核心代码

1 2 3 4 5 6 7 
AnyThinkBanner.framework AnyThinkInterstitial.framework AnyThinkNative.framework AnyThinkRewardedVideo.framework AnyThinkSDK.bundle AnyThinkSDK.framework AnyThinkSplash.framework 

版权声明:
作者:cc
链接:https://www.techfm.club/p/30105.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>