本节主要讲述广播订阅。
使用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包流程和处理与上节一样。