Making Java thread wait for value - java

I need to retrieve a users name from my Firebase database from their UID. This is my code for doing this, but I cannot work out how to get this working. The thread goes from:
final String[] returnName = new String[1];
Immediately to:
return returnName[0].toString();
Without waiting for it to populate data from the listener first, so the return value is null and the app crashes. Here is my full code for this module:
private synchronized String getFriendName(String key) {
final String[] returnName = new String[1];
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Profiles").child(key).child("Name");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
String userResult = datas.getKey();
if (userResult != null) {
returnName[0] = dataSnapshot.getValue().toString();
String temp = dataSnapshot.getValue().toString();
Toast.makeText(getBaseContext(), "Step 1: " + temp, Toast.LENGTH_SHORT).show();
Toast.makeText(getBaseContext(), "Step 2: " + returnName[0], Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return returnName[0].toString();
}
I have spent hours reading and trying stuff to get this to work but I cannot make the code actually execute properly first before firing off the return value.
Can anyone help me?
#TaherKorshidi
This is the code that calls this function:
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(currentLocation.latitude, currentLocation.longitude), radius);
geoQuery.removeAllListeners();
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
friendID = key;
String friend = getFriendName(friendID);
}
}
Answer, #TaherKorshidi the answer was to get the other values in the same listener. There is no other way around this and I wasn't sure how to do it until you pointed me in this direction. Working solution:
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
getFriendKey = key;
if (getFriendKey != ping_userID) {
for(ContactsList d : UserList){
if(d.getUID() != null && d.getUID().contains(getFriendKey)) {
friendName = d.getName();
}
}
Toast.makeText(getBaseContext(), "Name: " + String.valueOf(friendName), Toast.LENGTH_SHORT).show();
getFriendLocation(getFriendKey, friendName);
}
}

You can use asyncTask for this purpose as below
public static class GetFriendName extends AsyncTask<String, Void, Void>{
String returnName;
#Override
protected void onPreExecute() {
super.onPreExecute();
//Show progress bar
}
#Override
protected Void doInBackground(String... strings) {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Profiles").child(key).child("Name");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
String userResult = datas.getKey();
if (userResult != null) {
returnName = dataSnapshot.getValue().toString();
String temp = dataSnapshot.getValue().toString();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
//Dismiss progress bar
showFriendName(returnName);
}
}
you can call this class from onCreate() as below
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(currentLocation.latitude, currentLocation.longitude), radius);
geoQuery.removeAllListeners();
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
friendID = key;
new GetFriendName().execute(friendID);
}
}
then you can assign your friendName to a string value by using this method and do your work there
private static void showFriendName(String friendName){
Toast.makeText(stackOverflowActivity, ""+friendName, Toast.LENGTH_SHORT).show();
String friend = friendName;
//Do your work here
}
this method is calling from onPostExecute().

one way is to use CountDownLatch class:
private synchronized String getFriendName(String key) {
final String[] returnName = new String[1];
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Profiles").child(key).child("Name");
final CountDownLatch latch = new CountDownLatch(1);
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
String userResult = datas.getKey();
if (userResult != null) {
returnName[0] = dataSnapshot.getValue().toString();
String temp = dataSnapshot.getValue().toString();
Toast.makeText(getBaseContext(), "Step 1: " + temp, Toast.LENGTH_SHORT).show();
Toast.makeText(getBaseContext(), "Step 2: " + returnName[0], Toast.LENGTH_SHORT).show();
latch.countDown();
}
}
latch.countDown();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnName[0].toString();
}
although it solve your problem, but it is not a good solution.

Related

When I send a message I can see it in firebase but i cannot read it on emulator or my phone it show nothing

I am making chat app ... but when i send a message it sent and i can see it on firebase but in emuloter and phone i can't ... why it happen ? i think problem may be in readMessages()
This is my chat activity class
userQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String name = "" + ds.child("name").getValue();
hisImage = "" + ds.child("image").getValue();
nameTv.setText(name);
try {
Picasso.get().load(hisImage).placeholder(R.drawable.ic_defult_img_face).into(profileTv);
} catch (Exception e) {
Picasso.get().load(R.drawable.ic_defult_img_face).into(profileTv);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = messageEt.getText().toString().trim();
if (TextUtils.isEmpty(message)) {
Toast.makeText(ChatActivity.this, "Cannot send empty message...", Toast.LENGTH_SHORT).show();
} else {
sendMessage(message);
}
}
});
readMessages();
seenMessages();
}
read and send function
private void readMessages() {
chatList = new ArrayList<>();
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("Chats");
dbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
chatList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Modelchat chat = ds.getValue(Modelchat.class);
if (myUid.equals(chat.getReceiver()) && hisUid.equals(chat.getSender()) ||
hisUid.equals(chat.getReceiver()) && myUid.equals(chat.getSender())) {
chatList.add(chat);
}
adapterChat = new AdapterChat(ChatActivity.this, chatList, hisImage);
adapterChat.notifyDataSetChanged();
recyclerView.setAdapter(adapterChat);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void sendMessage(String message) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
String timeStamp = String.valueOf(System.currentTimeMillis());
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("Sender", myUid);
hashMap.put("receiver", hisUid);
hashMap.put("message", message);
hashMap.put("timeStamp", timeStamp);
hashMap.put("isSeen", false);
databaseReference.child("Chats").push().setValue(hashMap);
messageEt.setText("");
}
anyone can help me ?
if you want more code or please comment and i will submit it.
I will give some hints:
Move this in your readMessages():
chatList.clear();
And place it under this:
chatList = new ArrayList<>();
//here...
Make sure that your Modelchat looks like this:
class Modelchat{
//the main thing is that these must be written exactly like the keys in your database
private String Sender;
private String receiver;
private String message;
private String timeStamp;
private boolean isSeen;
public Modelchat(String Sender,String receiver,String message,String timeStamp,boolean isSeen){
this.Sender=Sender;
this.receiver=receiver;
this.message=message;
this.timeStamp=timeStamp;
this.isSeen=isSeen;
}
//generate also getters and setters......
}

Java AsyncTask Looking for an Alternative to Get() because i need it to loop in the thread itself rather than creating a new thread?

I need the doinBackground() to loop/wait for the data to be recieved before calling onPostExecute();
Data Calls From Firebase has a wait time. I know we have a get() function, but that kind of is a waist of milliseconds, I am looking for a workaround in which I don't need to do a boolean check, just simply wait for the data and then call the next thread from there. this way the transition can move smoothly.
private class GetDatafromFirebaseLoop extends AsyncTask<Void,Void,Void>{
private boolean dataRecieved = false;
String FirstName,MiddleName,LastName;
#Override protected void onPreExecute() {
User.Id = FirebaseAuth.getInstance().getCurrentUser().getUid();
runOnUiThread(new Runnable() {
#Override public void run() {
mTitle.setText("UserId : ");
mQuestion.setText(User.Id);
}
});
super.onPreExecute();
}
#Override protected Void doInBackground(Void... voids) {
FirebaseDatabase.getInstance().getReference("UsersInfo").child(Users.Id).addListenerForSingleValueEvent((new ValueEventListener() {
#Override public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists() && dataSnapshot.getChildrenCount() > 0) {
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
if (map.get("FirstName") != null) {
FirstName = map.get("FirstName").toString();
}
if (map.get("MiddleName") != null) {
MiddleName = map.get("MiddleName").toString();
}
if (map.get("LastName") != null) {
LastName = map.get("LastName").toString();
}
}
}
#Override public void onCancelled(DatabaseError databaseError) {}
}));
return null;
}
#Override protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(dataRecieved){
dataRecieved = false;
mTitle.setText("FullName");
mQuestion.setText(FirstName + " " + MiddleName + " " + LastName);
New RunNewAsyncTask().execute();// Which also continues this same format for other data.
}
else {
execute(new Runnable() {
#Override public void run() {
doInBackground();
}
});
}
}
}

My app only gets the first message i send then stops displaying other nodes

I'm using firebase to store the messages under a randomly generated node , messages are successfully added to this node but, it only displays the first message sent.
Here's my class containing the listeners.
I tried changing the Single event listener in get chat id to a value event listener but it didn't solve the problem.
when I close the chat and open it again it shows the first message only.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid();
mMatchId = getIntent().getExtras().getString("MatchId");
mDatabaseUser = FirebaseDatabase.getInstance().getReference().child("Users").child(currentUserId).child("connections").child("matches").child(mMatchId).child("ChatId");
mDatabaseChat = FirebaseDatabase.getInstance().getReference().child("Chat");
getChatId();
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setHasFixedSize(false);
mChatLayoutManager = new LinearLayoutManager(ChatActivity.this);
mRecyclerView.setLayoutManager(mChatLayoutManager);
mChatAdapter = new ChatAdapter(getDataSetChat(), ChatActivity.this);
mRecyclerView.setAdapter(mChatAdapter);
mSendEditText = (EditText) findViewById(R.id.message);
mSendButton = (Button) findViewById(R.id.send);
mSendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sendMessage();
}
});
}
private void sendMessage() {
String sendMessageText = mSendEditText.getText().toString();
if(!sendMessageText.isEmpty()){
DatabaseReference newMessageDb = mDatabaseChat.push();
Map newMessage = new HashMap();
newMessage.put("createdByUser", currentUserId);
newMessage.put("text", sendMessageText);
newMessageDb.setValue(newMessage);
}
mSendEditText.setText(null);
}
private void getChatId(){
mDatabaseUser.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
chatId = dataSnapshot.getValue().toString();
mDatabaseChat = mDatabaseChat.child(chatId);
getChatMessages();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void getChatMessages() {
mDatabaseChat.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot t : dataSnapshot.getChildren()) {
String message = null;
String createdByUser = null;
if (t.child("text").getValue() != null) {
message = t.child("text").getValue().toString();
}
if (t.child("createdByUser").getValue() != null) {
createdByUser = t.child("createdByUser").getValue().toString();
}
if (message != null && createdByUser != null) {
Boolean currentUserBoolean = false;
if (createdByUser.equals(currentUserId)) {
currentUserBoolean = true;
}
ChatObject newMessage = new ChatObject(message, currentUserBoolean);
resultChat.add(newMessage);
mChatAdapter.notifyDataSetChanged();
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Database image
it is because you first time open the activity and your getChatId() function is called a single time and give you one single id of your single message. and then you pass it on addChildEventListener. so thats y it gives you the single messagr to your recyclerview. if you show ur database schema i will provide youy more specific query
List<Chat> chatMessageList = new ArrayList<>();;
DatabaseReference database = FirebaseDatabase.getInstance()
.getReference()
.child(Common.CHATS);
database.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
chatMessageList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Chat chat = snapshot.getValue(Chat.class);
assert chat != null;
chatMessageList.add(chat);
mChatAdapter = new ChatAdapter(context, chatMessageList);
mRecyclerView.setAdapter(mChatAdapter);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});

How to handle Asynchronous call in firebase

I have to return the response from the firebase to another function which will do further processing with that data.
public ArrayList<String> getDatas(String number) {
final ArrayList<String> requestList = new ArrayList<>();
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference reference = database.getReference("Users").child(number).child("request_list");
final DatabaseReference requestReference = database.getReference("Request");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i(TAG, "onDataChange: ");
for (final DataSnapshot data : dataSnapshot.getChildren()) {
Log.i(TAG, "onDataChange: data: " + data.getValue());
requestList.add(data.getValue().toString());
}
Log.i(TAG, "onDataChange: for ended");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return requestList;
}
Since the firebase operation is asynchronous I am not able to wait for the data to return. Can someone help me with this problem?
you can use a callback method
http://developer.android.com/reference/java/util/concurrent/Callable.html
By using Callable interfaces you can pass an argument as function I added a simple code snippet for understanding.
public class MainActivity<V> extends Activity {
Callable<String> doLogin=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doLogin=new Callable<String>() { //created but not called now.
#Override
public String call() throws Exception {
//make some piece of code
return "something"; //or false
}
};
CheckSession checkSession=new CheckSession("sessionName");
String sessionKey="";
try { //we are sending callable to the DAO or any class we want
sessionKey=checkSession.getSessionKey(doLogin);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CheckSession{
String sessionName="";
Callable<String> func=null;
public CheckSession(String sessionName) {
super();
this.sessionName = sessionName;
}
public String getSessionKey(Callable<String> doLogin) throws Exception{
func=doLogin;
return (String) func.call();
}
}

How to access (modify, add elements) to an ArrayList/Collection in an Inner class in java?

I've got this search function to search for specific Users using a firebase Database using GeoFire.
What i want to do :
In GeoQueryEventListener() & onKeyEntered, I want to add each key found to an ArrayList (outside onKeyEntered()).
The problem i have :
When I print it inside the onKeyEntered(), it works.
But as soon as I exit the GeoQueryEventListener(), keysGeolocated is empty
public ArrayList<User> search(GeoFire geoFire, int day, final boolean isBartender, double latitude, double longitude, double radius, double rate, double nightlyRate, double hourlyRate, final String speciality){
keysGeolocated = new ArrayList<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
root = database.getReference();
DatabaseReference usersRef = root.child("Users");
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(latitude, longitude), radius);
geoQuery.addGeoQueryEventListener(new MyGeoListener(keysGeolocated));
System.out.println("KEYS GEOLOCATED" + keysGeolocated);
final ArrayList<User> result = new ArrayList<>();
for(String key : keysGeolocated){
Query queryByLocation = usersRef.orderByKey().equalTo(key);
DatabaseReference refToLocationQuery = queryByLocation.getRef();
if(checkRefNull(refToLocationQuery) == true) {
System.out.println("QUERY BY LOCATION NULL");
continue;
}
Query queryByTypeOfUser = refToLocationQuery.orderByChild("isBarTender").equalTo(isBartender);
DatabaseReference refToTypeOfUserQuery = queryByTypeOfUser.getRef();
if(checkRefNull(refToTypeOfUserQuery) == true) {
System.out.println("QUERY BY TYPE NULL");
continue;
}
Query queryByRate = refToTypeOfUserQuery.orderByChild("rate").startAt(rate);
DatabaseReference refToRateQuery = queryByRate.getRef();
if(checkRefNull(refToRateQuery) == true) {
System.out.println("QUERY BY RATE NULL");
continue;
}
DatabaseReference finalRef = null;
if(isBartender == true){
if(nightlyRate == 0){
Query queryByPrice = refToRateQuery.orderByChild("nightlyRate").endAt(nightlyRate);
finalRef = queryByPrice.getRef();
}
else if(hourlyRate == 0){
Query queryByPrice = refToRateQuery.orderByChild("hourlyRate").endAt(hourlyRate);
finalRef = queryByPrice.getRef();
}
//TODO Impelement checks for available dates
}
else{
finalRef = refToRateQuery;
}
finalRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue()!=null){
User userFound = dataSnapshot.getValue(User.class);
if(isBartender == true){
Bartender barTenderFound = (Bartender) userFound;
if(speciality != null){
if(barTenderFound.getSpeciality().contains(speciality)){
result.add(barTenderFound);
}
}
else{
result.add(barTenderFound);
}
}
else{
result.add(userFound);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
return result;
}
public boolean checkRefNull(DatabaseReference ref){
final boolean[] result = new boolean[1];
result[0] = false;
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("TEST1");
if(dataSnapshot.getValue()==null){
result[0] = true;
System.out.println("TESTTEST");
}
else{
System.out.println("TESTTEST TRUE" + dataSnapshot.getValue());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return result[0];
}
public void setBarTender(boolean barTender){
this.barTender = barTender;
}
}
class MyGeoListener implements GeoQueryEventListener {
private ArrayList<String> keysGeolocated;
MyGeoListener(ArrayList<String> keysGeolocated){
this.keysGeolocated = keysGeolocated;
}
#Override
public void onKeyEntered(String key, GeoLocation location) {
keysGeolocated.add(key);
}
#Override
public void onKeyExited(String key) {
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
}
#Override
public void onGeoQueryReady() {
}
#Override
public void onGeoQueryError(DatabaseError error) {
}
}
Make a Class which extends that listener and override all the methods
class MyGeoListener extends GeoQueryEventListener{
private ArrayList<String> keysGeolocated;
MyGeoListener(ArrayList<String> keysGeolocated){
this.keysGeolocated = keysGeolocated;
}
#Override
public void onKeyEntered(String key, GeoLocation location) {
keysGeolocated.add(key);
System.out.println("1111111111 : "+keysGeolocated);
System.out.println("2222222222 : "+key);
}
#Override
public void onKeyExited(String key) {
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
}
#Override
public void onGeoQueryReady() {
}
#Override
public void onGeoQueryError(DatabaseError error) {
}
}
Now use this class as follows:
geoQuery.addGeoQueryEventListener(new MyGeoListener(keysGeolocated));
The immediate next call will always print no values in the list as the listener is asynchronous and takes time to fill the list.
Try putting the print inside a handler postdelayed with say 30000 milliseconds delay. You will probably see entries in the list as by then the listener events should have fired.
Since an arraylist is mutable and you are passing it's reference, the arraylist in the argument will also be modified when the listener calls.
Good luck and let me know if you still have issues.

Categories

Resources