Do I stuck in Looper.java? - android

I have a JsonObjectRequest Get Method (volley), it was working fine, I got an JsonObject and used it for further actions but now it doesn't do what I want..
In the same class like the Get Request, I have the Method processResponse, which creates an "Account" Object and adds it to the Manager List:
private void processResponse(JSONObject response) {
try {
Log.e("processResponse", " called");
final Account user = new Account("","","");
String username = response.getString("Username");
String email = response.getString("Email");
String id = response.getString("Id");
user.setUsername(username);
user.setEmail(email);
user.setId(id);
Manager.AddObjectToUserList(user);
Manager Class with List:
class Manager {
private static List<Account> _ListofUsers = new ArrayList<Account>();
static void AddObjectToUserList(Account acc)
{
_ListofUsers.add(acc);
}
List<Account> getListOfUsers(){
return _ListofUsers;
}
}
I get an JsonObject from the GetResponse, it does successfully process the Response and creates an Object but when i want to return the List in Manager to another class, the "return List" is 0, I don't get the problem, since it was working fine before.
In debug mode, it jumps after the return List into the Looper.java and it doesn't leave this class..

This is happening because of asynchronous behaviour. You are trying to add the object to list after you are creating the list, that's why you are getting 0. If you want to use that list, you need to use it in processResponse method. Please see also this post
Hope it helps.

Related

How to display data in Fragment and Activity (both independent) from same Snapshot Listener (Firebase)

Would like to have your help on my weird problem that currently I am facing. I tried for couple of days but no luck and finally decided to post here to take help.
I created a Snapshot Listener attached to a Collection in Firebase defined as follows :-
public class FirebaseTypingStatusLiveData extends LiveData<List<documentSnapshot>> {
// Logging constant
private static final String TAG = "FirebaseQueryLiveData";
// Document Reference
private final DocumentReference documentReference;
// Listener
private final MyDocumentListener listener = new MyDocumentListener();
// Handler
private final Handler handler = new Handler();
private ListenerRegistration listenerRegistration;
// Flag to remove listener
private boolean listenerRemovePending = false;
private MutableLiveData <List<documentSnapshot> mutableLiveData = new MutableLiveData<>();
// Constructor
public FirebaseTypingStatusLiveData(DocumentReference documentReference) {
this.documentReference = documentReference;
}
public LiveData<List<documentSnapshot>> checknow(){
// Add listener
if (!Listeners.LIVESAMPLE.containsKey(documentReference)) {
listenerRegistration = documentReference.addSnapshotListener(listener);
Listeners.LIVESAMPLE.put(documentReference, listenerRegistration);
} else {
listenerRegistration = Listeners.LIVETYPINGSTATUSSAMPLE.get(documentReference);
}
return mutableLiveData;
}
// Listener definition
private class MyDocumentListener implements EventListener<DocumentSnapshot> {
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot, #Nullable
FirebaseFirestoreException e) {
Log.d(TAG, "onEvent");
// Check for error
if (e != null) {
// Log
Log.d(TAG, "Can't listen to query snapshots: " + documentSnapshot
+ ":::" + e.getMessage());
return;
}
setValue(documentSnapshot);
mutableLiveData.setValue(documentSnapshot);
}
}
}
}
The snapshot reads the data perfectly and advised as and when data is available.
The snapshot data is getting displayed 1. in Fragment (not part of Activity that i am talking about) 2. Activity through two view models that have the same code as follows :
#NonNull
public LiveData<List<documentSnapshot>> getDataSnapshotLiveData() {
Firestore_dB db = new Firestore_dB();
DocumentReference docref = db.get_document_firestore("Sample/"+docID);
FirebaseTypingStatusLiveData firebaseTypingStatusLiveData = new
FirebaseTypingStatusLiveData(docref);
return firebaseTypingStatusLiveData.checknow();
}
The Fragment & Activity code is also same except changing owner which are as follows :-
LiveData<List<documentSnapshot>> liveData = viewmodel.getDataSnapshotLiveData();
liveData.observe(this, new Observer<List<documentSnapshot>>() {
#Override
public void onChanged(DocumentReference docreef) {
String name = docreef.get("name");
stringname.setText(name); // The text is displaying either in Fragment or in Activity but not in both.
});
My problem is i need data in both i.e. Fragment & Activity whereas I am getting data either in Fragment or in Activity depending upon the code which I commented.
Kindly advise where I am making mistake. Thanks in Advance
Honestly, I am not sure that my answer wouldn't lead you away to the false way, but you can try.
My guess is that your problem could be somehow connected with ViewModel sharing.
There is a well-known task How to share Viewmodel between fragments.
But in your case, that can't help, because you have to share ViewModel between activities (now you have two separate ViewModels and that could be problem with Firestore EventListeners).
Technically you can share ViewModel between activities (I haven't try since usually I use Single activity pattern). For that as a owner parameter in ViewModelProvider constructor you can set instance of your custom Application class (but you have implement interface ViewModelStoreOwner for it). After that both in your activity and in your fragment you can get the same ViewModel with the Application class-instance:
val sharedViewModel = ViewModelProvider(mainApplication, viewModelFactory).get(SharedViewModel::class.java)
I made LiveData static that listens to changes in source data and provide updated content were ever required in different Activity.

Get variable value from method to outside

private String u_id;
private String u_name;
#Override
public void onSuccess(LoginResult loginResult) {
if(Profile.getCurrentProfile() == null) {
mProfileTracker = new ProfileTracker() {
#Override
public void onCurrentProfileChanged(Profile profile, Profile profile2) {
// profile2 is the new profile
u_id = profile2.getId().toString();
u_name = profile2.getName().toString();
mProfileTracker.stopTracking();
}
};
// no need to call startTracking() on mProfileTracker
// because it is called by its constructor, internally.
}
else {
Profile profile = Profile.getCurrentProfile();
u_id = profile.getId().toString();
u_name = profile.getName().toString();
}
/*new CreateNewProduct().execute();*/
/*updateFacebookButtonUI();*/
}
I want get value u_id and u_name to add arraylist, but it return null. I tried log have result. I need way resolve. Thanks :(
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("u_id", u_id)); // return null
params.add(new BasicNameValuePair("u_name", u_name)); // return null
Yes, adding "" results will not be null, but I want to get the value of the variable inside the onSuccess method to pass to the List below. The list below is in another class in same file with on Success
Variables u_id and u_name can be null only if they were not initialised. You don't set their value when adding the field to the class.
private String u_id; //only type and name set, not the value
I think if you're sure their value have to be set somewhere in the inner methods (doesn't matter in if or else either), it's not so dangerous to leave it like this. Also I would bet you're calling params.add(...)... after the above lines, unless it would cause the variables being null too...
But if you really want to get something back from your variables, you can initialise them as plain empty strings (""),
private String u_id = ""; //the variable's value is set too
and maybe later handle the "empty-string" checking before adding their value to your List<NameValuePair> variable; however it's not necessary. The main thing is: this way you can be 100% sure your variables won't be null...
EDIT #1: I don't know much about the structure of your file which contains onSuccess() method and the class with your list inside of it, but I would say it's another Java class file (MyClass.java or similar). I'm not an expert in Android multithreading, but I would say there is not anything that could block your inner class with your list to access the fields in the outer base class. You can reach fields anytime and anywhere in a class, no matter you want to do it in that very class or in the 10th nested inner class or method.
So unless your code does not look similar to this, I would think my first answer (above the edit) should be the solution. Maybe you should provide the code of your full class(es) to find the best solution, not just parts like you did first time.

return an object Android

I want to return an object with some things in them.
Here is the declaration;
Object user_det = get_user_det();
Here is the function code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
// TODO Auto-generated method stub
Iterable<DataSnapshot> rs = snap_user.getChildren();
Iterator<DataSnapshot> irs = rs.iterator();
long allNum2 = snap_user.getChildrenCount();
int maxNum2 = (int)allNum2;
int count_user = 1;
while(irs.hasNext())
{
if(count_user <= maxNum2)
{
Firebase user_data = new Firebase("https://myapp.firebaseio.com/");
AuthData authData = user_data.getAuth();
Map<String, Object> nPost = (Map<String, Object>) irs.next().getValue();
String db_email = nPost.get("email_addr").toString();
if (authData != null) {
String usr_email = authData.getProviderData().get("email").toString();
if(usr_email.equals(db_email))
{
//NB: I WANT TO ADD THE FOLLOWING INTO THE OBJECT
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
}
} else {
System.out.println("Failed");
}
}
count_user++;
}
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
I want to return the string disp_name and real_name but they are inside the addListenerForSingleValueEvent, so how do I get them out and return it to the function.
I have wrote "NB" in the code where I need help with.
Thanks for your time.
If you want to return an object from your method in java, do it like this:
The Object class:
This contains the structure of your Object, and defines what data will be in it. Also includes methods to easily get the data.
private class myObject {
private String name;
private String realName;
//The constructor, so you can set the data when creating the Object.
public myObject (String disp_name, String real_name) {
name = disp_name;
realName = real_name;
}
//Getter methods, to get the data.
public String getRealName() {return realName;}
public String getDisplayName() {return name;}
}
Your code:
private Object get_user_det() {
myObject o; //Declare it, so it can be returned.
...
String disp_name = nPost.get("disp_name").toString();
String real_name = nPost.get("real_name").toString();
o = new myObject(disp_name, real_name); //create it and set the data.
...
return myobject; //return the new Object with the data.
}
To get the data from the Object:
myObject o = get_user_det(); //Call the metod which return our Object.
String realName = o.getRealName(); //Get the data from the Object.
String displayName = o.getDisplayName;
In your case, it would be much easier to use a String array.
Hope this helps.
It's probably easiest to see what's going on, if you add some printlns to your code:
private Object get_user_det() {
Firebase f_user = new Firebase("https://myapp.firebaseio.com/User/");
System.out.println("Adding listener");
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot snap_user) {
System.out.println("Data received");
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
System.out.println("Returning");
return null; //NB: I NEED TO RETURN THE OBJECT HERE.
}
If you execute this code, you will see that it logs:
Adding listener
Returning
Data received
Most likely, this is not what you expected. But hopefully, it makes sense if you read my explanation below.
Asynchronous loading
When you register your listener:
f_user.addListenerForSingleValueEvent(new ValueEventListener(){
You tell Firebase to start listening for events. It goes off and starts retrieving the data from the server.
Since retrieving the data may take some time, it does this retrieval asynchronously so that your thread isn't blocked. Once the data is completely retrieved, Firebase calls the onDataChange method in your listener.
Between the time you start listening and the time onDataChange is called, your code continues executing. So there is no way to return data that is loaded asynchronously, because by the time your function returns, the data isn't loaded yet.
Solutions
Disclaimer: I am not an expert at solving this problem in Java, so there may be problems with my solutions. If^H^HWhen you find any, please report them in the comments.
I know of three possible solutions to the problem:
force the code to wait for the data to be returned
return a Future that at some point will contain the data
pass a callback into get_user_det and call that function once the data is available
You will probably be tempted to selected option 1, since it matches most closely with your mental modal of loading data. While this is not necessarily wrong, keep in mind that there is a good reason that the loading is done asynchronously. It might be worth taking the "learning how to deal with asynchronicity" penalty now.
Instead of writing up examples for all solutions, I'll instead refer to some relevant questions:
Retrieving data from firebase returning NULL (an answer that uses approach 3)
Is waiting for return, ok?
Java wait() & notify() vs Android wait() & notify() (a question from a user taking approach 1)
How it works:
Firebase uses reflection to build a JSON tree object to save to the database. When you retrieve this JSON tree, you can cast it back to your original object. Just like serializing and deserializing. This means you do not need to handle the keys and values when trying to "rebuild" your object like you are. It can all be done like so:
YourObject object = (YourObject) dataSnapshot.getValue(YourObject.class);
Notice the YourObject.class in the getValue(). This tells firebase to reflect through this class and find the appropriate accessors with the dataSnapshot.
How to do it
Be sure that your object has:
Accessors Appropriate getters and setters for ALL fields - (or annotated with #JsonIgnore if you wish to not save a particular field)
Empty constructor. Your object must provide a constructor that does not modify itself at all.
What your object should look like:
public class YourObject {
private String displayName;
private String realName;
public YourObject() { /*Empty constructor needed for Firebase */ }
// Accessors
public void setRealName(String realName){
this.realName = realName;
}
public String getRealName(){
return this.realName;
}
public String getDisplayName(){
return this.displayName;
}
public void setDisplayName(String displayName){
this.displayName = displayName;
}
}
Then, in any of the firebase callbacks, you can just cast your DataSnapshot in to your object:
public void onDataChange(DataSnapshot snap_user) {
YourObject object = new Object;
if(snap_user.getValue() != null) {
try {
object = (YourObject) snap_user.getValue(YourObject.class); <-- Improtant!!!
} catch(ClassCastException ex) {
ex.printStackTrace();
}
}
return object;
}
Also
It seems you are retrieving many objects. When doing this, I find it best to use the onChildEventListener then for each of the YourObjects in that node, onChildAdded(DataSnapshot ds, String previousChild); will be called.

Refresh context in Android

I don't know if I understand the problem correctly, but I have a weird issue. I have list which I am getting from Parse.com server and I filter the data by "tags". Sometimes I change "tags" with DialogFragment and refresh the lists, but after 2-3 change the list is showing wrong data. I have debugged it and it seems that wrong data is being taken from Database, which is weird because in another Fragment window the same method returns correct answers.
Here is how I get list from Parse :
YoursEventsAsync yoursEventsAsync = new YoursEventsAsync(Constants.LIMIT, getActivity());
yoursEventsAsync.asyncResponse = this;
yoursEventsAsync.execute();
Then, in this AsyncTask I am getting data like this :
#SafeVarargs
#Override
protected final ArrayList<Event> doInBackground(ArrayList<Event>... params) {
return ParseAdapter.getInstance().getEventsFromServer(limit, context);
}
And this is my main function responsible for getting sometimes good sometimes bad data :
public ArrayList<Event> getEventsFromServer(int limit, Context context) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Event");
query.whereContainedIn("tags", DatabaseAdapter.getTagNames(context));
query.setLimit(limit);
query.include("createdBy");
query.orderByDescending("createdAt");
ArrayList<Event> events = new ArrayList<>();
try {
List<ParseObject> parseEvents = query.find();
events = setEventList(parseEvents);
} catch (ParseException e) {
e.printStackTrace();
}
return events;
}
I have debugged the 3rd line and it sometimes works fine and sometimes not.
Here is DatabaseAdapter.getTagNames method :
public static List<String> getTagNames(Context context) {
Realm realm = Realm.getInstance(context);
RealmQuery<Tag> query = realm.where(Tag.class);
RealmResults<Tag> tagResults = query.findAll();
List<String> tags = new ArrayList<>();
for (Tag tag : tagResults) {
tags.add(tag.getName());
}
return tags;
}
As I have said earlier, if I open another fragment, which uses the same method 'getTagNames' it works fine. That's why I think the problem is with context. Is it possible that AsyncTask is somehow remembering the context and retrieveing the old one?

Return value inside Facebook request OnComplete

My doubt is, when i call a method below, the value is null, of course, because the object user it's not already populated, because will be populated inside Facebook request. So, the question is, how can i wait for the facebook complete and then return the right arraylist of users?
public ArrayList<User> getUsers(){
ArrayList<User> users = new ArrayList<User>();
User user = new User();
//It's not the complete code
Facebook.resquest(){
#Override
onComplete(){
for(int i = 0;i<10;i++){
users.add(user)
}
}
}
// The question is, what do i have to do for the below object 'll have the same value of user object inside facebook request?
return users;
}
Use Request.executeAndWait(request);
in your code, before the return.

Categories

Resources