Unity与Android交互通信系列(6)

  本节主要讲述广播订阅。

  使用AndroidJavaProxy代理接口,我们可以很方便的实现对Android端事件的订阅,灵活的进行Android端与Unity端的代码交互。通过AndroidJavaProxy代理,我们也可以在安卓端使用 BroadcastReceiver 接收广播,然后利用自定义的方法传递给C#端,但这种方式不太通用,我们希望能直接在C#端注册Android广播,下面演示一个通用的广播接收器。

  其原理依然是利用AndroidJavaProxy接口进行桥接,但通过Unity端注入action(Android端广播动作名称)参数,即由C#端控制需要注册的广播,Android端接收到广播之后,进行相应的数据处理(方便将这些数据传递到C#端),然后调用接口方法,触发C#端广播处理方法。首先我们看一下调用逻辑,代码如下:

//代码6-1
//C#端代码
using System.Collections.Generic;
using UnityEngine;
public class BroadcastExample : MonoBehaviour, UnityBroadcastHelper.IBroadcastListener
{
    UnityBroadcastHelper helper;
    void Start()
    {
         helper = UnityBroadcastHelper.Register( new string[] { "some_action_name" }, this);
    }
    public void OnReceive(string action, Dictionary<string, string> dictionary)
    {
        //广播处理逻辑
    }
    void OnDisable()
    {
        helper.Stop();
    }
}

  通过代码13-17可以看到广播注册界面非常简洁,脚本实现了UnityBroadcastHelper.IBroadcastListener 接口,在Start()方法中初始化一个UnityBroadcastHelper 对象,然后通过OnReceive()方法等待Android端回调并进行相应处理即可,使用完后,通过OnDisable()方法停止广播监听。

  C#端UnityBroadcastHelper类代码如下:

//代码6-2
//C#端代码
using System.Collections.Generic;
using UnityEngine;
public class UnityBroadcastHelper
{
    //Java端对象
    private readonly AndroidJavaObject javaObject;
    //构建函数,生成唯一Java端对象实例
    private UnityBroadcastHelper(string[] actions, IBroadcastListener listener)
    {
        ListenerAdapter adapter = new ListenerAdapter(listener, this);
        javaObject = new AndroidJavaObject("com.example.davidwang.UnityBroadcastHelper", actions, adapter);
    }

    //注册广播
    public static UnityBroadcastHelper Register(string[] actions, IBroadcastListener listener)
    {
        return new UnityBroadcastHelper(actions, listener);
    }
    //取消广播订阅
    public void Stop()
    {
        javaObject.Call("stop");
    }
    //广播订阅者接口
    public interface IBroadcastListener
    {
        void OnReceive(string action, Dictionary<string, string> dictionary);
    }
    //AndroidJavaProxy实现桥接,处理数据并触发订阅方法
    private class ListenerAdapter : AndroidJavaProxy
    {
        readonly IBroadcastListener listener;
        readonly UnityBroadcastHelper helper;
        public ListenerAdapter(IBroadcastListener listener, UnityBroadcastHelper helper) : base("com.example.davidwang.UnityBroadcastHelper$BroadcastListener")
        {
            this.listener = listener;
            this.helper = helper;
        }
        void onReceive(string action)
        {
            AndroidJavaObject javaObject = helper.javaObject;
            if (!javaObject.Call<bool>("hasKeyValue"))
            {
                return;
            }
            string[] keys = javaObject.Call<string[]>("getKeys");
            string[] values = javaObject.Call<string[]>("getValues");
            javaObject.Call("pop");
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            int n = keys.Length;
            for (int i = 0; i < n; i++)
            {
                dictionary[keys[i]] = values[i];
            }
            listener.OnReceive(action, dictionary);
        }
    }
}

  UnityBroadcastHelper类是接口的辅助类,其内部定义了IBroadcastListener接口及桥接类ListenerAdapter,并定义了注册与取消注册方法,从而简化调用端的使用。

  Java端代码如下:

//代码6-3
//Java端代码
package com.example.davidwang;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import java.util.LinkedList;
import java.util.Queue;
//引入UnityPlayer类
import com.unity3d.player.UnityPlayer;

public class UnityBroadcastHelper {
    private static final String TAG = "UnityBroadcastHelper";
	//接口定义
    public interface BroadcastListener {
        void onReceive(String action);
    }
    private final BroadcastListener listener;
    private Queue<String[]> keysQueue = new LinkedList<>();
    private Queue<String[]> valuesQueue = new LinkedList<>();
	//构造函数,注入Unity端广播订阅者并注册广播
    public UnityBroadcastHelper(String[] actions, BroadcastListener listener) {
        this.listener = listener;
        IntentFilter intentFilter = new IntentFilter();
        for (String action : actions) {
            intentFilter.addAction(action);
        }
        Context context = UnityPlayer.currentActivity;
        if (context == null) {
            return;
        }
        context.registerReceiver(broadcastReceiver, intentFilter);
    }
    public boolean hasKeyValue() {
        return !keysQueue.isEmpty();
    }
	//返回健
    public String[] getKeys() {
        return keysQueue.peek();
    }
	//返回值
    public String[] getValues() {
        return valuesQueue.peek();
    }
    public void pop() {
        keysQueue.poll();
        valuesQueue.poll();
    }
	//取消广播注册
    public void stop() {
        Context context = UnityPlayer.currentActivity;
        if (context == null) {
            return;
        }
        context.unregisterReceiver(broadcastReceiver);
    }
	//广播处理并回调Unity中OnReceive()方法
    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            MyLog.d(TAG, "UnityBroadcastHelper: action: " + action);
            Bundle bundle = intent.getExtras();
            if (bundle == null) {
                bundle = new Bundle();
            }
            int n = bundle.size();
            String[] keys = new String[n];
            String[] values = new String[n];
            int i = 0;
            for (String key : bundle.keySet()) {
                keys[i] = key;
                Object value = bundle.get(key);
                values[i] = value != null ? value.toString() : null;
                i++;
            }
            keysQueue.offer(keys);
            valuesQueue.offer(values);
            listener.onReceive(action);
        }
    };
}

  UnityBroadcastHelper类中的BroadcastListener接口即为需要桥接的接口,在该类的构造函数中传入了C#端定义的action及桥接实例对象并进行了广播注册操作,在stop()方法中取消了广播注册,UnityBroadcastHelper通过持有BroadcastReceiver,将广播注册到Context上下文中。为在两端进行数据传递,将Extra的Bundle数据映射为C#中的Dictionary,传递给 OnReceive()方法,方便C#代码使用。为演示简单,这里将所有类型的数据都映射成string类型,实际使用时可根据需要进行处理,如通过AndroidJavaObject进行复杂类型数据传递。

  通过使用该通用类,不仅可以注册自定义广播,也可以注册系统广播(可能需要权限申请),还可以一次性注册多个广播,界面简洁,使用简单。


提示

         本示例也需要引入UnityPlayer类,该类位于Unity提供的Classes.jar包,引入Jar包流程和处理与上节一样。

相关推荐

  1. UnityAndroid交互通信系列6

    2024-06-18 09:52:02       5 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-18 09:52:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-18 09:52:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-18 09:52:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-18 09:52:02       20 阅读

热门阅读

  1. idea git stash报错Too many revisions specified

    2024-06-18 09:52:02       7 阅读
  2. 创建单例模式的六种方式

    2024-06-18 09:52:02       9 阅读
  3. jQuery 常用函数解析

    2024-06-18 09:52:02       7 阅读
  4. MVVM模式理解(基于Qt分析)

    2024-06-18 09:52:02       5 阅读
  5. 11 类型泛化

    2024-06-18 09:52:02       6 阅读
  6. 游戏服务器要注意哪些安全事项?

    2024-06-18 09:52:02       8 阅读
  7. 【杂记-浅谈VRRP虚拟路由冗余协议】

    2024-06-18 09:52:02       6 阅读