简而言之,我正在制作一个应用程序,用于在Swipe Cards上获取当前用户的最后已知位置和显示附近用户(半径30公里范围内)。我正在使用 FireBase 和 GeoFire 来完成此任务。在我实现位置查询之前,我只使用
onChildAdded()
侦听器从数据库中获取所有用户 - 它运行正常。
但是,当我添加使用另一组侦听器的位置查询时,我开始得到随机且意外的结果。例如重复 - 它将获取所有用户两次,然后在两张后续卡上显示它们,即我将第一次刷用户A,然后同一用户再次出现在下一张卡上。为了增加混乱,这有时只会发生。我是GeoFire的新手,但是我怀疑听众是以某种方式发生冲突。
这是我的代码:
1)在onCreate()中,我检查位置权限。随后,我每隔两分钟使用FusedLocationProviderClient请求位置更新。一切正常,正如预期的那样。
2)每隔两分钟,我收到当前位置并触发此onLocationResult()回调:
// location callback - get location updates here:
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Log.d("MainActivity", "onLocationResult triggered!");
for(Location location : locationResult.getLocations()) {
mCurrentLocation = location;
Log.d("MainActivity", "Lat: " + mCurrentLocation.getLatitude() + ", Long: " + mCurrentLocation.getLongitude());
// write current location to geofire:
mGeofireDB = FirebaseDatabase.getInstance().getReference("Locations");
GeoFire geofire = new GeoFire(mGeofireDB);
geofire.setLocation(mCurrentUserID, new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude()), new GeoFire.CompletionListener() {
@Override
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.d("MainActivity", "There was an error saving the location to GeoFire: " + error);
} else {
Log.d("MainActivity", "Location saved on server successfully!");
// check current user sex:
checkSex();
// find nearby users of the current user's location:
getNearbyUsers();
// here's rest of the code which takes the list of cards created by getNearbyUsers() and displays those cards
3)一旦该位置已成功写入数据库,我调用checkSex()方法,该方法不言自明,工作正常。然后,我试图在下面的函数中使用GeoQuery来吸引附近的用户。
// get all nearby users:
private void getNearbyUsers() {
Log.d("MainActivity", "getNearbyUsers() triggered!");
mLocationsDB = FirebaseDatabase.getInstance().getReference().child("Locations");
GeoFire geoFire = new GeoFire(mLocationsDB);
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude()), mCurrentUserProximityRadius);
geoQuery.removeAllListeners();
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
// user has been found within the radius:
@Override
public void onKeyEntered(String key, GeoLocation location) {
Log.d("MainActivity", "User " + key + " just entered the radius. Going to display it as a potential match!");
nearbyUsersList.add(key);
}
@Override
public void onKeyExited(String key) {
Log.d("MainActivity", "User " + key + " just exited the radius.");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mCardList.removeIf(obj -> obj.getUserID().equals(key));
mCardArrayAdapter.notifyDataSetChanged();
Log.d("MainActivity", "User " + key + " removed from the list.");
} else {
Log.d("MainActivity", "User should have exited the radius but didn't! TODO support older versions of Android!");
}
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
// all users within the radius have been identified:
@Override
public void onGeoQueryReady() {
displayPotentialMatches();
}
@Override
public void onGeoQueryError(DatabaseError error) {
}
});
}
// end of getNearbyUsers()
我检索半径内的所有用户(onKeyEntered())并将它们添加到nearbyUsersList。一旦找到所有用户并将其添加到列表中(还有另一个监听器 - onGeoQueryReady()),我最终调用displayPotentialMatches()方法,在那里我检查数据库中的用户是否在nearUsersList中,如果是,我将它们添加到mCardList并通知适配器有关更改:
// retrieve users from database and display them on cards, based on the location and various filters:
private void displayPotentialMatches() {
Log.d("MainActivity", "displayPotentialMatches() triggered!");
mUsersDB.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d("MainActivity", "displayPotentialMatches ON CHILD ADDED listener triggered!");
// check if there is any new potential match and if the current user hasn't swiped with them yet:
if (dataSnapshot.child("Sex").getValue() != null) {
if (dataSnapshot.exists()
&& !dataSnapshot.child("Connections").child("NoMatch").hasChild(mCurrentUserID)
&& !dataSnapshot.child("Connections").child("YesMatch").hasChild(mCurrentUserID)
&& !dataSnapshot.getKey().equals(mCurrentUserID)
// TODO display users based on current user sex preference:
&& dataSnapshot.child("Sex").getValue().toString().equals(mCurrentUserOppositeSex)
// location check:
&& nearbyUsersList.contains(dataSnapshot.getKey())
) {
String profilePictureURL = "default";
if (!dataSnapshot.child("ProfilePictureURL").getValue().equals("default")) {
profilePictureURL = dataSnapshot.child("ProfilePictureURL").getValue().toString();
}
// POPULATE THE CARD WITH THE DATABASE INFO:
Log.d("MainActivity", dataSnapshot.getKey() + " passed all the match checks!");
Card potentialMatch = new Card(dataSnapshot.getKey(), dataSnapshot.child("Name").getValue().toString(), profilePictureURL);
mCardList.add(potentialMatch);
mCardArrayAdapter.notifyDataSetChanged();
}
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
} // end of displayPotentialMatches()
此代码后面是一段代码,它接受该列表并在卡片上显示这些用户。在我实现GeoQuerying之前测试了这段代码并且工作正常,所以我不在此处。它仍在位置结果回调中。
我很确定如果我不需要使用这么多的监听器(onKeyEntered,onGeoQueryReady,onChildAdded),它将按预期工作。我只是从onGeoQueryReady监听器中获取数据库中的所有数据,一旦准备就绪,将执行在卡上显示用户的代码。
但是,由于你需要使用监听器,即使从FireBase获取数据 - onChildAdded(我得到它 - 它是实时的),它会导致意外的结果/重复。有时它有效,有时我会得到重复(连续两张卡上的相同用户),如上所述。但是只在onChildAdded侦听器中调用notifyDataSetChanged。
我在这里缺少什么?听众是否会以某种方式发生冲突(例如,一个被调用而另一个没有第一个被完成)?或者是在没有完成所有侦听器的情况下显示调用卡上的用户的代码段,因此在GeoQueryReady和onChildAdded上触发它?如果是这种情况,有没有办法只在两个监听器完成后执行这段代码?
如果您还有其他需要,请与我们联系,例如:日志的屏幕截图。任何帮助将非常感激。
答案 0 :(得分:1)
首先,您不需要在位置更改时始终初始化地理位置实例。其次不要等待侦听器中的地理查询准备就绪。
将这些变量设为级别:
mGeofireDB = FirebaseDatabase.getInstance().getReference("Locations");
mUsersDB = FirebaseDatabase.getInstance().getReference("users");
GeoFire geofire = new GeoFire(mGeofireDB);
GeoQuery geoQueryNearByUser=null;
GeoQueryEventListener geoQueryEventListener=new GeoQueryEventListener() {
// user has been found within the radius:
@Override
public void onKeyEntered(String key, GeoLocation location) {
Log.d("MainActivity", "User " + key + " just entered the radius. Going to display it as a potential match!");
nearbyUsersList.add(key);
mUsersDB.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
if (dataSnapshot.child("Sex").getValue() != null) {
if (dataSnapshot.exists()
&& !dataSnapshot.child("Connections").child("NoMatch").hasChild(mCurrentUserID)
&& !dataSnapshot.child("Connections").child("YesMatch").hasChild(mCurrentUserID)
&& !dataSnapshot.getKey().equals(mCurrentUserID)
// TODO display users based on current user sex preference:
&& dataSnapshot.child("Sex").getValue().toString().equals(mCurrentUserOppositeSex)
// location check:
&& nearbyUsersList.contains(dataSnapshot.getKey())
) {
String profilePictureURL = "default";
if (!dataSnapshot.child("ProfilePictureURL").getValue().equals("default")) {
profilePictureURL = dataSnapshot.child("ProfilePictureURL").getValue().toString();
}
// POPULATE THE CARD WITH THE DATABASE INFO:
Log.d("MainActivity", dataSnapshot.getKey() + " passed all the match checks!");
Card potentialMatch = new Card(dataSnapshot.getKey(), dataSnapshot.child("Name").getValue().toString(), profilePictureURL);
mCardList.add(potentialMatch);
mCardArrayAdapter.notifyDataSetChanged();
}
}
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
@Override
public void onKeyExited(String key) {
Log.d("MainActivity", "User " + key + " just exited the radius.");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mCardList.removeIf(obj -> obj.getUserID().equals(key));
mCardArrayAdapter.notifyDataSetChanged();
Log.d("MainActivity", "User " + key + " removed from the list.");
} else {
Log.d("MainActivity", "User should have exited the radius but didn't! TODO support older versions of Android!");
}
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
// all users within the radius have been identified:
@Override
public void onGeoQueryReady() {
}
@Override
public void onGeoQueryError(DatabaseError error) {
}
};
我更改了上面的geoquery,以便只要用户的密钥可用,就可以获取该特定用户数据并将卡添加到适配器。
位置回拨:
在位置回拨仅使用最后一个位置。 循环使用位置数组会导致异常,因为您一次触发多个地理查询侦听器
// location callback - get location updates here:
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Log.d("MainActivity", "onLocationResult triggered!");
mCurrentLocation = locationResult.getLastLocation();
Log.d("MainActivity", "Lat: " + mCurrentLocation.getLatitude() + ", Long: " + mCurrentLocation.getLongitude());
// write current location to geofire:
geofire.setLocation(mCurrentUserID, new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude()), new GeoFire.CompletionListener() {
@Override
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.d("MainActivity", "There was an error saving the location to GeoFire: " + error);
} else {
Log.d("MainActivity", "Location saved on server successfully!");
// check current user sex:
checkSex();
// find nearby users of the current user's location:
getNearbyUsers();
}
}
}
}
};
我们不是再次初始化查询,而是检查查询实例是否存在,只用用户当前位置更新查询中心。
// get all nearby users:
private void getNearbyUsers() {
Log.d("MainActivity", "getNearbyUsers() triggered!");
GeoLocation currentLocationGeoHash = new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
if(geoQueryNearByUser == null){
geoQueryNearByUser = geoFire.queryAtLocation(currentLocationGeoHash, mCurrentUserProximityRadius);
geoQueryNearByUser.addGeoQueryEventListener(geoQueryEventListener);
}
else {
geoQueryNearByUser.setCenter(currentLocationGeoHash);
}
}
// end of getNearbyUsers()