美文网首页
7、与iOS、Android的交互 高级篇

7、与iOS、Android的交互 高级篇

作者: 万士辰 | 来源:发表于2017-07-09 02:44 被阅读942次

    本文属于「Unity与iOS、Android平台的整合」系列文章之一,转载请注明出处。主要讲解如何优雅地实现Unity与iOS、Android的交互。

    零、前言

    市面上提供的SDK基本是原生iOS、Android,在Unity接入过程中,往往不是简简单单地一次调用,而是成套的原生API接入,需要一套完整的解决方案来处理这些问题。
    在此,将自己的方案提出,意为抛砖引玉,如果大家有其他思路还望留言交流。
    该解决方案的是对基础原理进行的拓展。
    4、与iOS、Android的交互 理论篇
    5、与iOS、Android的交互 实践篇——主动调用
    6、与iOS、Android的交互 实践篇——传递参数

    一、需求分析

    实际项目使用中,我们希望

    • 使用C#代码调用相关功能
    • 在调用时不需要区分具体平台
    • 在编辑器模式下进行模拟测试
    • 实现复杂类型参数相互传递
    • 实现监听
    • 实现回调
    • ……

    二、方案设计

    <<<需求:

    • 使用C#代码调用相关功能

    >>>方案:

    将代码根据职责进行分层:

    • 上层逻辑(C#)
    • 供上层逻辑调用的接口(C#)
    • 调用SDK的接口(C++、Java)
    • SDK API(OC、Java)

    <<<需求:

    • 在调用时不需要区分具体平台
    • 在编辑器模式下进行模拟测试

    >>>方案:

    将上一个需求的方案进行扩充:

    • 供上层逻辑调用的接口(C#),使用编译开关在函数内部根据平台区别调用
        public void Login()
        {
            Login_();
        }
        #if UNITY_EDITOR
        private static void Login_()
        {
            Debug.Log("SDK Login");
        }
        #elif UNITY_IOS
        [DllImport("__Internal")]private static extern void Login_();
        #elif UNITY_ANDROID
    private static void Login_() 
    {
        using (AndroidJavaClass jc = new AndroidJavaClass("xxx.xxx.xxx))
        {
            jc.CallStatic("Login_"); 
        }
    }
        #endif
    

    <<<需求:

    • 实现复杂类型参数相互传递

    >>>方案:

    • 使用Json序列化、反序列化相关类型
    • 相互传递JSON字符串
    public class Message
    {
        public int id;
        public string name;
        public static string ToJson(Message msg)
        {
            return Json.Serialize(msg);
        }
        public static Message FromJson(string json)
        {
            return Json.DeSerialize<Message>(json);
        }
    }
    public void SetLoginInfo(Message msg)
    {
        SetLoginInfo_(Message.ToJson(msg));
    }
    #if UNITY_EDITOR
    private static void SetLoginInfo_(string msg)
    {
        Debug.Log("SDK SetLoginInfo" + msg);
    }
    #elif UNITY_IOS
    [DllImport("__Internal")]private static extern void SetLoginInfo_(string msg);
    #elif UNITY_ANDROID
    private static void SetLoginInfo_()
    {
        using (AndroidJavaClass jc = new AndroidJavaClass("xxx.xxx.xxx))
        {
            jc.CallStatic("SetLoginInfo_", msg); 
        }
    }
    #endif
    public Message GetLoginInfo()
    {
        Message.FromJson(GetLoginInfo_());
    }
    #if UNITY_EDITOR
    private static string GetLoginInfo_()
    {
        return "{\"id\":1,\"name\":\"abc\"}";
    }
    #elif UNITY_IOS
    [DllImport("__Internal")]private static extern string GetLoginInfo_();
    #elif UNITY_ANDROID
    private static string GetLoginInfo_()
    {
        using (AndroidJavaClass jc = new AndroidJavaClass("xxx.xxx.xxx))
        {
            return jc.CallStatic<string>("GetLoginInfo_"); 
        }
    }
    #endif
    

    <<<需求:

    • 实现监听

    >>>方案:

    • 供上层逻辑调用的接口(C#),提供监听
    • 调用SDK的接口(C++、Java),注册原生API的监听
    • 使用UnitySendMessage调用指定C#方法从而触发C#层的监听
    • 使用唯一的GameObject来负责接收UnitySendMessage
      供上层逻辑调用的接口(C#)
    public class XXXSDKEventHandler : Monobehaviour
    {
        public event Action onLogin;
        public void OnLogin()
        {
            if(onLogin != null)
            {
                onLogin();
            }
        }
    }
    public class XXXSDKManager
    {
        private XXXSDKEventHandler handler;
        public XXXSDKManager()
        {
            handler = new GameObject("XXXSDKEventHandler").AddComponent<XXXSDKEventHandler>();
            Object.DontDestroyOnLoad(handler.gameObject);
        }
        public event Action onLogin
        {
            add { handler.onLogin += value; }
            remove { handler.onLogin -= value; }
        }
    }
    

    调用SDK的接口(C++)

    void OnLogin(){
        UnitySendMessage("XXXSDKEventHandler", "OnLogin", "");
    }
    

    调用SDK的接口(Java)

    void OnLogin(){
        UnityPlayer.UnitySendMessage("XXXSDKEventHandler", "OnLogin", "");
    }
    

    <<<需求:

    • 实现回调

    >>>方案:

    • 供上层逻辑调用的接口(C#),缓存回调并分配回调ID
    • 调用SDK的接口(C++、Java),接收回调ID
    • 使用UnitySendMessage调用指定C#方法从而触发C#层的回调
    • 使用唯一的GameObject来负责接收UnitySendMessage
    public class XXXSDKEventHandler : Monobehaviour
    {
        private int cba_key = 0;
        private Dictionary<int, Action> cbas = new Dictionary<int, Action>();
        public int AddCallbackAction(Action action)
        {
            cbas.Add(cba_key, action);
            return cba_key++;
        }
        private void Receiver(string jsonMessage)
        {
            JsonObject jo = Json.DeserializeObject<JsonObject>(jsonMessage);
            int key = int.Parse(jo["cba_key"].ToString());
            if (!cbas.ContainsKey(key)) { return; }
            Action<bool> action = cbas[key];
            if (action != null) { action(result); }
            cbas.Remove(key);
        }
        #region 延时回调
        Queue<string> callbackQueue = new Queue<string>();
        private void MessageHandle(string json)
        {
            JsonObject jo = Json.DeserializeObject<JsonObject>(json);
            string funcName = jo["funcName"].ToString();
            string message = jo["message"].ToString();
            this.callbackQueue.Enqueue(funcName);
            this.callbackQueue.Enqueue(message);
        }
        void Update()
        {
            while (true)
            {
                yield return null;
                while (callbackQueue.Count >= 2)
                {
                    SendMessage(this.callbackQueue.Dequeue(), this.callbackQueue.Dequeue());
                }
            }
        }
        #endregion
    }
    public class XXXSDKManager
    {
        private XXXSDKEventHandler handler;
        public XXXSDKManager()
        {
            handler = new GameObject("XXXSDKEventHandler").AddComponent<XXXSDKEventHandler>();
            Object.DontDestroyOnLoad(handler.gameObject);
        }
        public Login(Action onFinish)
        {
            Login_(handle.AddCallbackAction(onFinish));
        }
        #if UNITY_EDITOR
        private static void Login_(int callbackID)
        {
            Debug.Log("SDK Login");
            handler.SendMessage("Receiver","{\"funcName\":\"Receiver\",\"message\":\"{\\\"cba_key\\\":\\\"" + callbackID + "\\\"}\"}");
        }
        #elif UNITY_IOS
        [DllImport("__Internal")]private static extern void Login_(int callbackID);
        #elif UNITY_ANDROID
        private static void Login_(int callbackID) 
        {
            using (AndroidJavaClass jc = new AndroidJavaClass("xxx.xxx.xxx))
            {
                jc.CallStatic("Login_", callbackID); 
            }
        }
    }
    

    SDK的回调接口(C++)

    void Login_(int callbackID){
        //处理xxx,最后调用以下
        UnitySendMessage("XXXSDKEventHandler", "OnLogin", [NSString stringWithFormat:@"{\"funcName\":\"Receiver\",\"message\":\"{\\\"cba_key\\\":\\\"%d\\\"}\"}", callbackID]);
    }
    

    SDK的回调接口(Java)

    void Login_(int callbackID){
        //处理xxx,最后调用以下
        UnityPlayer.UnitySendMessage("XXXSDKEventHandler", "OnLogin", "{\"funcName\":\"Receiver\",\"message\":\"{\\\"cba_key\\\":\\\"" + callbackID + "\\\"}\"}");
    }
    

    三、收个尾

    以上内容为Unity与iOS/Android原生SDK交互的整套解决方案,能够解决复杂的原生SDK交互需求,在项目中也经历过实践,亲测可用。

    相关文章

      网友评论

          本文标题:7、与iOS、Android的交互 高级篇

          本文链接:https://www.haomeiwen.com/subject/feotpttx.html