3

I am trying to get the document that is stored in Firebase Cloud Firestore to my custom object(animal). I need to pass this object to another Activity. I used intent.putextra("class name", obj) function but I can't reach the object that i created to get a document. Here is my code:

public void start(View view){

String id;
EditText animalIdEditText = findViewById(R.id.animalIDsearch);
String animalId = animalIdEditText.getText().toString();
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docRef = db.collection("Animals").document(animalId);
docRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
    @Override
    public void onSuccess(DocumentSnapshot documentSnapshot) {
        Animal animal = documentSnapshot.toObject(Animal.class); // this is where I create animal object. It doesn't let me declare it above either.
    }
});

    Intent intent = new Intent(this, searchActivity.class);
    intent.putExtra("Animal",animal); // error in this line : animal  can't be resolved
    startActivity(intent);
}
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
asls2
  • 385
  • 1
  • 4
  • 9
  • Firebase APIs are asynchronous and return immediately while data is being accessed. You need to design for that by only using fetched data within the callback. https://medium.com/google-developers/why-are-the-firebase-apis-asynchronous-e037a6654a93 – Doug Stevenson Mar 20 '18 at 17:15
  • @asls2 did you manage to make this work? – Levi Moreira Mar 21 '18 at 18:48
  • yeah , thanks for the help. – asls2 Mar 21 '18 at 19:10
  • @asls2 As I, understand you are a beginner. Think that future visitors like you, will visit this post and they'll think that passing an object to a method will solve the asynchronous behaviour of `onSuccess()` method and it doesn't... – Alex Mamo Mar 23 '18 at 09:23
  • @asls2 Thanks for doing the right thing. – Alex Mamo Mar 24 '18 at 11:25
  • @asls2 I see that you've changed your answer and that is completely ok. I'm not going to get into details, but to quote this meta post https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work "The bottom line is that you should accept the answer that you found to be the most helpful to you, **personally**.". Don't let anyone tell you which answer you should accept or not, bottom line, it's **your** decision, after all it's **your** question. – Levi Moreira Mar 24 '18 at 11:59
  • First of all, I appreciated both of you for the anwers and detailed explanation that enlightened me, as a beginner. It took me time to see difference between your answers. The reason I changed my answer is Alex's code let me make read operation on two separate collections and send them to another activity at the same time. Levi's answer was checked as helpful as first because it met my needs at first time but then I had to switch to call back method as Alex explained. That's why I changed the answer. I couldn't be more thankful for the time you spend to solve my problem. Thanks again. – asls2 Mar 25 '18 at 12:15

1 Answers1

1

To solve this, you need to wait for the data and to achieve this, you need to create a custom callback like this:

public interface MyCallback {
    void onCallback(Animal animal);
}

Then you need to create method that looks something like this:

public void readData(MyCallback myCallback) {
    String id;
    EditText animalIdEditText = findViewById(R.id.animalIDsearch);
    String animalId = animalIdEditText.getText().toString();
    FirebaseFirestore db = FirebaseFirestore.getInstance();
    DocumentReference docRef = db.collection("Animals").document(animalId);
    docRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            Animal animal = documentSnapshot.toObject(Animal.class);
            myCallback.onCallback(animal);
        }
    });
}

And in the end, just simply call readData() method and pass an instance of the MyCallback interface as an argument wherever you need it in your activity like this:

readData(new MyCallback() {
    @Override
    public void onCallback(Animal animal) {
        Intent intent = new Intent(this, searchActivity.class);
        intent.putExtra("Animal", animal);
        startActivity(intent);
    }
});

For more information, you can take a look at my answer from this post and also at this video.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • This also worked. Sadly, I can't mark two of the answers as helpful. Thank you for the detailed explanation though. Appreciated! – asls2 Mar 21 '18 at 19:11
  • Could you explain the need for a second callback here? – Levi Moreira Mar 22 '18 at 10:24
  • @LeviAlbuquerque Is not a second callback, it's the first and a single callback, that needs to be used in order to use that object outside that method. See my comments above. – Alex Mamo Mar 22 '18 at 11:18
  • I don't understand the need for the callback, that's why I used "second" callback because the first callback would be the onSuccess by firebase. I don't understand why add the extra layer of complexity if calling a method from the current class would be much easier – Levi Moreira Mar 22 '18 at 11:46
  • Like they do in this snippet from google> https://github.com/firebase/quickstart-android/blob/master/firestore/app/src/main/java/com/google/firebase/example/fireeats/RestaurantDetailActivity.java. They have an onEvent callback and they call a method from the current class onRestaurantLoaded – Levi Moreira Mar 22 '18 at 11:49
  • 1
    I see that you still don't understand the need of the callback but this is the way asynchronous calls work. "calling a method from the current class would be much easier", you are right. But if needed outside? If needed outside, the callback is needed. In that example, they aren't using those values outside because there aren't needed. Why to use a callback? In OP's code, he's trying to use the `animal` object outside. – Alex Mamo Mar 22 '18 at 12:00