android - 无法在 Unity 中使用原生 android 模块(AAR 文件)进行广播
问题描述
我的想法是我需要在后台使用 GPS 来进行统一项目。它用于查明用户在远足某些路线时是否靠近“信息点”。为此,我们认为(因为我们需要获得相当频繁的 GPS 更新)使用地理围栏。我在 Android Studio 中创建了一个原生模块,并将 AAR 文件统一放置。但是广播接收器永远不会触发。我从 Android 的后台广播教程开始作为基本模板(https://developer.android.com/training/location/geofencing.html)。代码最终看起来像这样:
桥接器.java
package com.BrabantWater.mynativemodulegps;
//import needed packages / classes.
import android.annotation.SuppressLint;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
public class Bridge extends Application
{
public static Context context;
public static String notificationContentTitle = "";
public static String notificationContentText = "";
public static String notificationDescription = "";
private GeofencingClient geofencingClient;
private ArrayList<Geofence> geofenceList;
GeofencingRequest.Builder builder;
PendingIntent geofencePendingIntent;
GeofencingRequest geofencingRequest;
public Bridge(Context ctx)
{
context = ctx;
geofencingClient = new GeofencingClient(context);
geofenceList = new ArrayList<>();
builder = new GeofencingRequest.Builder();
}
public Bridge(Context ctx, String contentTitle, String contentText, String description)
{
context = ctx;
notificationContentTitle = contentTitle;
notificationContentText = contentText;
notificationDescription = description;
geofencingClient = new GeofencingClient(context);
geofenceList = new ArrayList<>();
builder = new GeofencingRequest.Builder();
}
public void addGeoFence(String id, double latitude, double longitude)
{
geofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId(id)
.setCircularRegion(
latitude,
longitude,
300
)
.setNotificationResponsiveness(5000)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT |
Geofence.GEOFENCE_TRANSITION_DWELL)
.setLoiteringDelay(1)
.build());
Log.i("TESTTAG", "GEOFENCE toegevoegd aan lijst");
Log.i("TESTTAG", geofenceList.toString());
}
@SuppressLint("MissingPermission")
public void GeoFenceCompleted()
{
Log.i("TESTTAG", "Completing Geofence");
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL);
builder.addGeofences(geofenceList);
geofencingRequest = builder.build();
geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Geofences added
// ...
Log.i("TESTTAG", "Geofences added");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to add geofences
// ...
Log.e("TESTTAG", "Couldn't add geofences");
}
});
}
public void removeAllGeoFences()
{
geofencingClient.removeGeofences(getGeofencePendingIntent())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Geofences removed
// ...
Log.i("TESTTAG", "removed all geofences");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to remove geofences
// ...
Log.e("TESTTAG", "Couldn't remove all geofences");
}
});
}
public void removeGeoFences(List<String> ids) {
geofencingClient.removeGeofences(ids);
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (geofencePendingIntent != null) {
Log.i("TESTTAG", "used old intent for GeofenceBroadcastReceiver");
return geofencePendingIntent;
}
Log.i("TESTTAG", "Created new intent for GeofenceBroadcastReceiver");
Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
geofencePendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
return geofencePendingIntent;
}
}
地理围栏广播接收器.java
package com.BrabantWater.mynativemodulegps;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofenceStatusCodes;
import com.google.android.gms.location.GeofencingEvent;
import java.util.List;
import androidx.core.app.NotificationCompat;
import static android.content.ContentValues.TAG;
import static androidx.core.content.ContextCompat.getSystemService;
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
// ...
@Override
public void onReceive(Context context, Intent intent) {
Log.i("TESTTAG", "GEOFENCE received");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = GeofenceStatusCodes.getStatusCodeString(geofencingEvent.getErrorCode());
Log.e(TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) {
// Get the geofences that were triggered. A single event can trigger
// multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = triggeringGeofences.toString();
// Send notification and log the transition details.
sendNotification(geofenceTransitionDetails, context);
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
Log.e(TAG, "Transition Error");
}
}
private void sendNotification(String geofenceTransitionDetails, Context context)
{
Log.i("TESTTAG", "GEOFENCE triggered");
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "Brabant_Water_GO")
.setContentTitle(Bridge.notificationContentTitle)
.setContentText(Bridge.notificationContentText)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Brabant_Water_GO";
String description = Bridge.notificationDescription;
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel("Brabant_Water_GO", name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(context, NotificationManager.class);
notificationManager.createNotificationChannel(channel);
notificationManager.notify(10000, builder.build());
Log.i("TESTTAG", "notification Shown");
}
}
}
清单如下所示:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.BrabantWater.mynativemodulegps">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application>
<receiver android:name=".GeofenceBroadcastReceiver" />
</application>
</manifest>
在 Unity 方面,我创建了一个调用桥接方法的类并修改了清单以确保我可以访问该位置。
AndroidCaller.cs
#if UNITY_ANDROID
//basic imports.
using UnityEngine;
public class AndroidCaller : MonoBehaviour
{
AndroidJavaClass unityPlayerClass;
AndroidJavaObject unityActivity;
AndroidJavaObject bridge;
object[] parameters;
void Start()
{
//just done for testing purposes
CallNativePlugin();
}
//method that calls our native plugin.
public void CallNativePlugin()
{
// Retrieve the UnityPlayer class.
unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
// Retrieve the UnityPlayerActivity object ( a.k.a. the current context )
unityActivity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
// Setup the parameters we want to send to our native plugin.
parameters = new object[4];
parameters[0] = unityActivity;
parameters[1] = "Title";
parameters[2] = "ContentText";
parameters[3] = "Description";
// Retrieve the "Bridge" from our native plugin.
// ! Notice we define the complete package name.
bridge = new AndroidJavaObject("com.BrabantWater.mynativemodulegps.Bridge", parameters);
// Call addGeoFence in bridge, with our parameters.
bridge.Call("addGeoFence", "id", 51.461059, 4.310962);
bridge.Call("addGeoFence", "id2", 51.451459, 4.329905);
bridge.Call("GeoFenceCompleted");
}
}
#endif
AndroidManifest.xml(在 Unity 项目中)
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<application>
<receiver android:name="com.BrabantWater.mynativemodulegps.GeofenceBroadcastReceiver" />
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
</manifest>
在使用 logcat 进行大量测试后,我无法弄清楚为什么 onReceive 从未被触发。所以我就这样被卡住了。
解决方案
我从没想过我自己会得到一个答案。显然,您必须在接收器中为统一的 xml 以及 android studio 中的 xml 使用全名,如下所示:
<receiver android:name="com.BrabantWater.mynativemodulegps.GeofenceBroadcastReceiver" />
推荐阅读
- forms - 表单中的动态收件人
- python - 如何让按钮自行删除?
- spring - 如何在 Spring JPA 原生查询中替换 SQL 查询条件
- python - 在文件顶部导入的模拟函数
- performance - Is there a theorem like CAP for web development?
- tensorflow - 在 Keras 中,是否有文档描述了初始化器、优化器等的类映射的字符串名称?
- python - 我想以当前登录用户作为申请人附加我的表格
- json - 通过 Java 进行 Amazon Neptune SPARQL 查询:我可以获得原始 JSON 响应吗?
- batch-file - 将命令导出到其他批处理文件时出现问题
- obiee - 在不使用 SQL 的情况下获取 OBIEE 中的总和