添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • TopOn : 移动广告变现收益提升工具 | 广告平台聚合管理 | 数据监测分析
  • TopOn 提供多种平台 SDK 集成,支持 Android、iOS、Unity、Cocos2dx、CocosCreator。这里分析一下 Unity SDK 的实现原理,主要说明 Unity 与 Native 如何通信、框架文件结构。

  • TopOn SDK v5.8.13
  • Unity 2019.4.32f1
  • 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 后下载。

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

    SDK Demo

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

  • GitHub - toponteam/TopOn-Android-Demo at v5.8.13
  • GitHub - toponteam/TopOn-iOS-Pod-Demo at v5.8.13
  • TopOn Document
  • 使用 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
  • Unity - Manual: JAR plug-ins
  • C# 调用 Java 代码使用的是 AndroidJavaObject 来调用 Java 方法。

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

    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

    com.anythink.unitybridge.sdkinit.SDKInitHelper.initAppliction(final String appid, String appkey)
    
  • Unity - Manual: JAR plug-ins
  • Android to Unity

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

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

    public SDKInitHelper(SDKInitListener pSDKInitListener)
    public interface SDKInitListener {
        void initSDKSuccess(String str);
        void initSDKError(String str, String str2);
    

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

    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 代码

    extern "C" {
      float FooPluginFunction();
    
  • Unity - Manual: Building plug-ins for iOS
  • C# Assets/AnyThinkAds/Platform/iOS/Internal/Script/ATUnityCBridge.cs

    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

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

    *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
  • Unity - Manual: Building plug-ins for iOS
  • TopOn 这里实现得比较复杂,但是核心原理简单。通过将 C# 的回调方法注入到 Objective-C 中,并且将回调地址存储到 placementId 对应的 Value 字典中,后续回调时通过 placementId 查找到回调再调用。

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

    -(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

    [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 都是以最简的代码实现了一个例子,没有任何多余功能。

    Android

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

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

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

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

    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
    
  • 所有的代码都按照功能分别编译到单独的 Framework 文件中。
  • 针对每一个 SDK 都单独编写了 Native 的桥接代码,如:AnyThinkBaiduAdapter.framework
  • 桥接代码与 SDK 代码直接静态编译到了一起。
  • SDK 核心代码:

    AnyThinkBanner.framework
    AnyThinkInterstitial.framework
    AnyThinkNative.framework
    AnyThinkRewardedVideo.framework
    AnyThinkSDK.bundle
    AnyThinkSDK.framework
    AnyThinkSplash.framework