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

本节主要讲述广播订阅。

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

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

java 复制代码
//代码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类代码如下:

java 复制代码
//代码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端代码如下:

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包流程和处理与上节一样。

相关推荐
异次元的归来3 小时前
Unity DOTS中的share component
unity·游戏引擎
拭心4 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
向宇it6 小时前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
带电的小王6 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡6 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道7 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
_oP_i7 小时前
unity webgl部署到iis报错
unity
Go_Accepted7 小时前
Unity全局雾效
unity
向宇it7 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
阿甘知识库8 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站