When I manually add the objects for the data of the recycler view in the required ArrayList, it works fine, but as soon as I fetch the data from my Node JS MongoDB backend, it shows nothing.
The Java part:
Call<ArrayList<BookingDetails>> call = retrofitInterface.executeAdminPendingReq();
call.enqueue(new Callback<ArrayList<BookingDetails>>() {
#Override
public void onResponse(Call<ArrayList<BookingDetails>> call, Response<ArrayList<BookingDetails>> response) {
if(response.code() == 200){
bookingDetails = response.body();
Log.i("Something", bookingDetails.get(0).getPurpose());
Toast.makeText(PendingRequestActivity.this, "Something to show", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(PendingRequestActivity.this, "Nothing to show", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ArrayList<BookingDetails>> call, Throwable t) {
Toast.makeText(PendingRequestActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
The Node JS part:
app.post('/admin_pendingReq', async(req, res) => {
const cursor = bookingReqCollection.find({}, {projection: {_id: 0, confirm: 0}})
const results = await cursor.toArray()
if(results.length > 0){
res.status(200).send(JSON.stringify(results))
}else{
res.status(400).send()
}
})
Also, one thing to note is if I try to log the data fetched from Node in Android Studio (which is present above), it shows the required data.
EDIT: I solved the issue by setting the recycler view adapter and the layout manager inside of the onResponse method after taking it out from the onCreate method. But I would still like to know why this has happened
Related
I am using addvalueEventListener function to fetch data from firebase. I am trying to achieve AND operation by using this function. Issue is that when I am calling this function, it can filter data client side and also show in recyclerview but it can run if statement if data found and also run else part.
Suppose I want data which is available in firebase, it filters the data and show and also run else part data not found.
My main function code is:
private void GetFilterData(final String filter) {
reference = FirebaseDatabase.getInstance().getReference().child("Donation");
Query query = reference.limitToLast(20).orderByChild("username").equalTo(username);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//Here I can check if data is exist or not, if exist it shows data
if (dataSnapshot.exists())
{
arrayList.clear();
alert.setVisibility(View.GONE);
for (DataSnapshot dataSnapshot1:dataSnapshot.getChildren())
{
DonationHelper donationHelper = dataSnapshot1.getValue(DonationHelper.class);
//here I want to check again and filter data, if data exist, shows data and if does not exist required filter then run else part. But issue is that if data exist it shows data and also run its else part.
if (donationHelper.getAction().equals(filter))
{
pd.cancel();
arrayList.add(donationHelper);
}
else
{
//Filter data exist or not, everytime it run else part code
pd.cancel();
alert.setVisibility(View.VISIBLE);
alert.setText("Currently no request is in Queue");
//Toast.makeText(Track_Donation.this, "No data found", Toast.LENGTH_SHORT).show();
}
adapter = new DonationAdapter(Track_Donation.this,arrayList);
donationdata.setAdapter(adapter);
}
}
else
{
pd.cancel();
alert.setText("Start do Donation, and help Poor and Needy people's");
Toasty.error(Track_Donation.this, "You don't do any Donation yet", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
pd.cancel();
Toasty.error(Track_Donation.this, "Server Error", Toast.LENGTH_SHORT).show();
}
});
}
I am posting this question because I couldn't find any satisfactory answers online.
I am developing an Android App in which the data is fetched from the external server(in my case it's localhost MySQL server now) and displayed on the screen.
However, the constraint with this is that the person should always be connected to the internet to get all the data which can be viewed on the phone.
Now, what I would like to achieve is, once the data has been retrieved from the external server it should be stored on the device so that even though the user opens up the app without being connected to the internet, the previously fetched data should be showed to him.
In other words, I would like to have the offline capability.
How can I achieve this?
I have implemented the same for my app. Have a look at the code and you will understand how to do it. I have used Retrofit for the same. I have checked if the nursejson which is in sharedpreference is null. if it is null then continue further to hit API if not then load data from that sharedpreference.
To refresh the list, just check if you have internet connectivity and then delete that sharedpreference and call getnurse method again.
//After Oncreate
pref = getApplicationContext().getSharedPreferences("CachedResponse", 0);
editor = pref.edit();
editor.apply();
// Call getNurses method
getNurses();
//Method to get Nurses
public void getNurses() {
nurseJson = pref.getString("nurseJson", null);
if (nurseJson != null) {
progressBar.setVisibility(View.INVISIBLE);
gson = new Gson();
nurse = gson.fromJson(nurseJson, Nurse.class);
nurseList = nurse.getNurse();
namesArrayList.clear();
for (String nurses : nurseList) {
namesArrayList.add(nurses);
}
namesAdapter.notifyDataSetChanged();
} else {
Call<Nurse> call = apiInterface.getNursesList();
call.enqueue(new Callback<Nurse>() {
#Override
public void onResponse(Call<Nurse> call, Response<Nurse> response) {
progressBar.setVisibility(View.INVISIBLE);
onItemsLoadComplete();
if (response.isSuccessful()) {
nurse = response.body();
nurseJson = new Gson().toJson(nurse);
editor.putString("nurseJson", nurseJson);
editor.commit();
nurseList = nurse.getNurse();
namesArrayList.clear();
for (String nurses : nurseList) {
namesArrayList.add(nurses);
}
namesAdapter.notifyDataSetChanged();
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
}
#Override
public void onFailure(Call<Nurse> call, Throwable t) {
progressBar.setVisibility(View.INVISIBLE);
onItemsLoadComplete();
if (t.getLocalizedMessage() != null) {
if (t.getLocalizedMessage().contains("Unable to resolve host")) {
utility.createSnackbar(coordinatorLayout, "Please Check Internet Connection!");
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
}
});
}
}
I am working with Firebase for my Android app. I have successfully retrieved information in multiple places in my code using something like:
dbRef = new Firebase(Constants.URL_DB);
childRef = dbRef.child(Constants.KEY_CHILD);
objectRef = childRef.child(objectId);
// Log the URL to the object.
// This has been verified. The path is good.
Log.d("DEBUG", "To String: " + objectRef.toString());
childRef.addValueEventListener(new ValueEventListener()
#Override
public void onDataChange(DataSnapshot snapshot) {
Log.d("DEBUG", "Success");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("DEBUG", "Failure");
}
});
The exact same code has been use successfully in another activity but for some reason, it doesn't work in on particular activity (onDataChange is never called, neither is onCancelled). Could it be that The Firebase sessions in other activities are conflicting with this one? I have seen problems that could be resolved with:
Firebase.goOnline();
Firebase.goOffline();
Althought I am not sure I understand what those exactly do. Maybe it is because I somehow can't access a child added by push() with his id?
UPDATE:
I successfully wrote in the database at objectRef, but onDataChange() is still never called.
Ok I found what was causing the problem. I had an empty loop because I needed the information to continue and it somehow prevented the events from the database. I had something like this in the onCreate():
childRef.addValueEventListener(new ValueEventListener()
#Override
public void onDataChange(DataSnapshot snapshot) {
Log.d("DEBUG", "Success");
object = snapshot.getValue(MyObject.class);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("DEBUG", "Failure");
}
});
And I had the loop in the onResume():
while(object == null);
What I did to correct the issue was replacing the loop with:
if(object == null){
// Do stuff
}
And I added a call to onResume() in onDataChange()
I use Retrofit library to handle JSON response from my WCF web-service.
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
textView.setText(searchResult.toString());
} // end of success(
#Override
public void failure(RetrofitError error) {
showToast(R.string.internet_sikintisi);
}
});
I noticed that i get error if i leave the fragment or activity where i called this function from. Because I set text in textView where the activity or fragment is not already exists.
I modified the code to be like this:
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
try{
textView.setText(searchResult.toString());
} catch(Exception e) {}
} // end of success(
#Override
public void failure(RetrofitError error) {
try{
showToast(R.string.internet_sikintisi);
} catch(Exception e) {}
}
});
Basically, the problem solved, the app is not crashing every time the user gets in and out immediately. Still there might be a better solution for this problem.
I checked and Retrofit DOES NOT have cancel request feature!
You need to added checks success and failure methods the same way you would when using an asynctask check if the context still exist or make a isActive field that you set on onResume/onPause of the activity or fragment.
It is possible to cancel Retrofit requests in Retrofit 2(beta is available now.)
call.cancel();// as simple as that
I'm using a third party cloud service (Kinvey) to fetch data for my app. All Kinvey methods that service are wrapped in async classes. I'm currently getting an error log below and I understand why I am getting it. I just don't know how to resolve the problem.
Error :
03-12 13:41:03.449: E/AndroidRuntime(18375): FATAL EXCEPTION: main
03-12 13:41:03.449: E/AndroidRuntime(18375): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2130968661, class android.widget.ListView) with Adapter(class com.example.chartviewer.LazyAdapter)]
Here is the segment of my code where I notify my adapter after deleting content within an async method :
mKinveyClient.appData("artistdata", ArtistData.class).delete(artistID, new KinveyDeleteCallback() {
#Override
public void onSuccess(KinveyDeleteResponse result) {
Log.e("DELEET", "Data Deleted sucessfully");
Toast.makeText(getBaseContext(),
"Artist Deleted", Toast.LENGTH_SHORT).show();
//refreshes list
adapter.notifyDataSetChanged();
//displays dialog box if no artists left
displayNoArtist();
}
#Override
public void onFailure(Throwable error) {
Log.e("DELETE", "AppData.delete Failure", error);
}
});
Updating the adapter :
mKinveyClient.appData("artistdata", ArtistData.class).get(myQuery, new
KinveyListCallback<ArtistData>() {
#Override
public void onSuccess(ArtistData[] result) {
Log.e("FETCHING", "Data fetched sucessfully");
for (ArtistData data : result) {
String name= data.getArtistname();
String imageurl = data.getArtisturl();
String id = data.getArtistid();
artistIds.add(id);
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put(TAG_NAME, name);
hashMap.put(TAG_IMAGEURL, imageurl);
addMyArtists(hashMap);
}
displayData();
}
#Override
public void onFailure(Throwable error) {
Log.e("FETCHING", "AppData.get by Query Failure", error);
}
});
Adaptor creator code :
list = (ListView)findViewById(R.id.artistlist);
adapter = new LazyAdapter(MyArtistsActivity.this,"artists", artistItemList);
list.setAdapter(adapter);
registerForContextMenu(list);
Disclaimer: I am a member of Kinvey's engineering team.
The issue is that you are deleting from the artistItemList prior to deleting from the Kinvey backend, and then not calling adapter.notifyDatasetChanged until the callback. This results in the lag you noticed where the underlying dataset has changed while waiting for the callback from the backend Kinvey delete call. You need to group both of these calls together, and do it in one of two ways:
Make the artistID that you pass to the delete method final, and in onSuccess, remove the item from artistItemList before calling adapter.notifyDatasetChanged.
Call adapter.notifyDatasetChanged before calling the kinveyClient.appData().delete() method, immediately after removing the item from artistItemList. This option may cause issues if the delete fails on the Kinvey back-end.