I have a question about some functionalities of Firestore. I am trying to occupy an array of data obtained through multiple read operations from the Firestore database. Is there a way for me to be notified when all the data are successfully read and stored in my array? This is particularly an issue because read operations are not finished in the order that they are called. Here are some code that illustrates my problem:
/* My array to insert the data read from the Firestore database */
String[] my_array = new String[3];
/* A method that will be called to initialize our array */
private void initArray(String doc_one, String doc_two, String doc_three) {
initSingleIndex(0, doc_one);
initSingleIndex(1, doc_two);
initSingleIndex(2, doc_three);
}
private void initSingleIndex(final int index, String doc_id) {
/* We perform our read operation here */
question_ref.document(doc_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
my_array[index] = documentSnapshot.getString("some_field");
}
});
}
My current implementation is to keep a global counter, which will be used to keep track of how many read operations were successfully carried out. I am also wondering whether the onSuccess() callbacks can be fired concurrently, since this will then lead to data corruption (i.e. the classic problem of incrementing values concurrently).
Any help or suggestion will be appreciated.
Related
I am currently building an android app, that uses a small database which consists of two entity-data-classes (Card and CardDeck) and a third one representing a many to many relationsship between the former two by mapping there long id primary keys together (CardInCardDeckRelation).
Now I want to insert a new Deck into my database, which works just fine, but if I want to insert some CardInCardDeckRelation-objects afterwards by using the id returned from the insertCardDeck()-method it fails because the insertion calls on the relationship-table occur before the insertion of the cardDeck object is finished so I am always getting the wrong cardDeck-id.
I think I am going into the right direction by using a Async-task to insert my CardDeck however I don't know to proceed from there since I can only pass one set of Arguments to my async-task object, so I can't pass my Relationshipobjects to be inserted in say for example a onPostExecute-method in the Async-task-class.
This my insert-method in my Repository-class:
public void insertCardDeckAsync(CardDeck cardDeck){
new insertAsyncTaskDao(mCardDeckDao).execute(cardDeck);
}
private static class insertAsyncTaskDao extends AsyncTask<CardDeck, Void, Void> {
private CardDeckDao mAsyncTaskDao;
insertAsyncTaskDao(CardDeckDao dao){
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final CardDeck... params){
mAsyncTaskDao.insertCardDeck(params[0]);
return null;
}
#Override
protected void onPostExecute(Void v){
//maybe insert Relationship object here?
}
}
I would be very thankful if someone could provide a way to properly insert an entity object and some many-to-many relationsship objects afterwards, using the id generated by the former insert.
So after some testing i figured out my error:I was initially using an Executor which I defined somewhere else in my App to handle database operations, so I don't have to create a private inner class extending AsyncTask for every database operation in my Repository class.For some reason though my usage of Executor does seem to block the particular thread when executing database-queries so:
mExecutors.diskIO().execute(new Runnable(){
//insert new Deck
//insert Many-to-Many relationsship-object
}
would execute both operations immediately after one another, thus causing a SQL-ForeignConstraint-related error, because it tries to insert the realtion objects before the actual deck object is inserted.
The solution to this is to just use a AsyncTask instead, handling all the database operation in the right order in the doInBackground-method:
#Override
protected Void doInBackground(final CardDeck... params){
// insert new deck object into database
insertionId = mAsyncTaskDao.insertCardDeck(params[0]);
// create relations-array
CardInCardDeckRelation[] relations = new CardInCardDeckRelation[STANDARD_CARDS.length];
// insert standard-card objects into array
for(int i = 0; i < STANDARD_CARDS.length; i++){
relations[i] = new CardInCardDeckRelation(insertionId,
mAsyncCardDao.getStandardCardByName(STANDARD_CARDS[i]),
i);
}
// insert created array into database
mRelationDao.insertMultiple(relations);
Log.d(LOG_TAG, "Deck inserted");
return null;
}
If anyone needs further explanation I can provide the whole AsyncTask class.
I'm using the Task API in my app to retrieve data from Firebase Database, which is usually from different nodes. I have a helper class for Firebase Database like so:
public class FirebaseDbHelper {
public Task<DataSnapshot> getData() {
TaskCompletionSource<DataSnapshot> source = new TaskCompletionSource<>();
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference(FIRST_NODE).child(SUB_NODE);
dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
source.setResult(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
source.setException(databaseError.toException());
}
});
return source.getTask();
}
}
As you can see, getData() returns a Task object, which I use on my interactor class (I'm using the MVP architecture for my app) like so:
public class TestDbInteractor {
private FirebaseDbHelper mDbHelper;
private Listener mListener;
public TestDbInteractor(#NonNull Listener listener) {
mDbHelper = new FirebaseDbHelper();
mListener = listener;
}
void getData() {
mDbHelper.getData().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
mListener.onGetDataSuccess(new MyObject(task.getResult()));
} else {
mListener.onGetDataFailed(task.getException());
}
});
}
public interface Listener {
void onGetDataSuccess(MyObject object);
void onGetDataFailed(Exception exception);
}
}
This works as expected. However, we noticed a behavior that when retrieving a lot of data, even if the activity that started the task is already finish()ed, the task still proceeds and attempts to complete. This I believe, is something that could be considered as a memory leak, since a process is still going even though it's supposed to be stopped/destroyed already.
What's worse is that when I try to get a different data (using a different Task in a different activity to a different node in Firebase), we noticed that it waits for the previous task to complete first before proceeding with this new one.
To give more context, we're developing a chat app similar to Telegram, where users could have multiple rooms and the behavior we saw is happening when a user enters a room. This is the flow:
User enters room, I request data for the room details.
Upon getting the room details, I display it, then request for the messages. I only retrieve the most recent 10. During this time, I just show a progress bar on the activity.
In order for the message details to be complete, I get data from different nodes on Firebase, this is where I use Tasks mainly.
After getting the messages, I pass it on to the View, to display the messages, then I attach a listener for new messages. Everything works as expected.
The behavior I mentioned at the beginning is noticeable when the user does something like this:
User enters a room with messages, room details are retrieved instantly, messages are still loading.
User leaves the room (presses the back button), this gets the user back to the room list, and enters a different one.
At this point, the retrieval of the room details takes such a long time - which we thought was odd, since the data isn't really that big to begin with.
After a few more testing, we concluded that the long retrieval time was caused by the current task (get room details) is still waiting for the previous task (get messages) started in a different activity, to finish first before starting.
I attempted to implement my answer here, trying to use a CancellableTask, but I am at a loss on how to use it with my current implementation, where I use a TaskCompletionSource, where you could only set a result or an exception.
I was thinking this could work if I move the task completion source to the interactor class level instead of the helper -- I haven't tried it yet. I think it's possible, but would take a lot of time to refactor the classes I already have.
So I figure why not try Doug's answer, using activity-scoped listeners. So I tested it like below.
In my activity, I added a getActivity() method, which can be called in the presenter:
public class TestPresenter
implements TestDbInteractor.Listener {
private View mView;
private TestDbInteractor mDbInteractor;
#Override
void bindView(View view) {
mView = view;
mDbInteractor = new TestDbInteractor(this);
}
#Override
void requestMessages() {
mDbInteractor.getData(mView.getActivity());
}
// Listener stuff below
}
and updated my getData() like so:
void getData(#NonNull Activity activity) {
mDbHelper.getData().addOnCompleteListener(activity, task -> {
if (task.isSuccessful()) {
mListener.onGetDataSuccess(new MyObject(task.getResult()));
} else {
mListener.onGetDataFailed(task.getException());
}
});
}
Unfortunately, this doesn't seem to work though, exiting the activity still waits for the tasks to complete, before the new task initiated in a different activity starts.
If you kick off a query to Realtime Database, it will always run to completion, whether or not there are any listeners attached to the Task that was returned. There is no way to cancel that work, neither by removing the last listener manually, nor by using activity-scoped listeners that are removed automatically. Queries in motion stay in motion. Also, all traffic to and from RTDB is pipelined over a single socket, which implies that the results of subsequent queries after one that's incomplete will have to wait for the everything ahead of it in the queue to complete first. This is likely the root cause for your observation - you have an incomplete query that other queries are waiting on, regardless of your use of the Task API.
Fortunately, if you have persistence enabled, the second query should be served by the cache of the first query, and not require another round trip to the server.
If you need to make sure that you retain the results of the first query across configuration changes that destroy the activity, then you should use something like LiveData from the Android architecture components to manage this, so that you can pick up the query where it left off after a configuration change. If you do this, don't use activity-scoped listeners.
I've written a three-part blog post about using architecture components with Firebase, which may also be of interest.
Hey You can use childEventListener. use dataSnapshot.getChildrenCount().
dbFriend=FirebaseDatabase.getInstance().getReference("Friend");
dbFriend=dbFriend.child(mPreferences.getString("username","")).child("already");
dbFriend.addChildEventListener(new ChildEventListener() {
int already=0;
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Username u=dataSnapshot.getValue(Username.class);
already=alread+1;
if(already >= dataSnapshot.getChildrenCount()){
//get to know when data fetching got completed
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
I want to get a data from a kinvey Collection, put it in a db and then modify it locally.I want to be sure that all data is gotten from collection before any modification. My problem is that, getting collection is done via AsyncAppData which runs in background,which makes my task impossible.
A sample code is shown
//getting data
ArrayList<String> runfunc = new ArrayList<String>();
final AsyncAppData<EventEntityWhy> myevents4 = mKinveyClient.appData("WhyWorldTemp", EventEntityWhy.class);
myevents4.get(new KinveyListCallback<EventEntityWhy>() {
#Override
public void onSuccess(EventEntityWhy[]){
for (EventEntityWhy x1 : result) {
String temp1 = (String) x1.get("whyindex");
runfunc.add(temp1)
}
}
}
//then processing will start
//runfunc array will be processed here
That's not how asynchronous programming works, and Kinvey has no plans to change that paradigm. The proper way to build your code is by processing your data inside the onSuccess callback rather than on the mainthread.
What you could do is set an event flag inside the callback, and then you create a waiter at your "//then processing will start" point that waits for that flag.
I don't understand how is the Parse working?
I download data in parse to my arraylist , but when I show the Pets.size inside (//here) method "done" it will show 4, but when I show pets.size outside the done's method it will show 0?
public class Test extends AppCompatActivity {
ArrayList<Pet> pets;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
pets = new ArrayList<>();
ParseQuery<Pet> query = new ParseQuery<>("Pets");
query.findInBackground(new FindCallback<Pet>() {
#Override
public void done(List<Pet> list, ParseException e) {
if (e!=null){
Toast.makeText(Test.this,"Error",Toast.LENGTH_LONG).show();
}
for (Pet pet : list){
Pet newPet = new Pet();
newPet.setName(pet.getName());
newPet.setType(pet.getType());
pets.add(newPet);
}
// here
}
});
Toast.makeText(Test.this,"You have "+pets.size()+" pets",Toast.LENGTH_LONG).show();
}
Here's my Pet class:
#ParseClassName("Pets")
public class Pet extends ParseObject {
public String getName(){
return getString("name");
}
public void setName(String name) {
put("name", name);
}
public String getType(){
return getString("type");
}
public void setType(String type) {
put("type", type);
}
}
And an orther question , what should I do if I wanna save the data in local data?
Explanation:
findInbackground performs an operation to find all ParseObjects in a background thread (outside the main thread, or UI thread). So when it completes in the place where you have the comment
//here
That is when the background thread finishes it's call to find the objects. When you try to look at the size of the array outside that call where it shows size of 0, it is because it reached that point before the background thread finishes it's work (of adding to your array from objects it found).
What is happening is the operation for find() is happening in parallel with your main threads code.
And for your second question, make sure you enableLocalDatastore and then you can pin results from queries to your local cache. This data is stored on the device until the user deletes your app or clears cached data in settings.
Follow this guide to setup local cache Local Datastore with Parse
Note: A solution to your problem for when the background task of finding the pets is complete is to call a method from within the Callback for the findInBackground call that will handle the newly found Pet ParseObjects. Also remember to handle if the query fails either by finding no objects or some failure in connection / timeout.
just calling pet.pin() or pet.pinInBackground(); you can save a parseObject in local storage , to query objects in local storage you need set query.fromPin(true)
https://parse.com/docs/android/guide#objects-the-local-datastore
"done" method fires when the background task ends.
I am in process of developing an Android tablet app using sqllite 3.7.4 which would
perform following:
Fetches information from the UI
Performs some logic and store related information to the sqlite database
The stored information has to be send immediately OR at schedule
interval (ex. at 5:00 on xyz date) over the network
Currently, we have developed a dispacher mechanism (thread ), which constantly polls the database for new information inserted in the database. The thread fetches the information and send to the network module.
But, I feel this is not the correct approach as
Polling every time is a overhead. There can be times when there is nothing to execute
It is not real time , because we poll after every 5 seconds
So
Is there a way to send a trigger to my network module as soon as information is updated in database?
Or any better way to achieve this task?
Thanks in advance.
This question is about one year ago, but i think this is a common problem. This is how i handled the Database changes:
In my Adapter ( SQL ADAPTER) i have methods for updating / deleting or inserting data into the Database obviously. Like this method:
public long addProduct(String code, String name ... String gid, String gdate) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_CODE, code);
initialValues.put(KEY_NAME, name);
...
initialValues.put(KEY_CHECK, this.checkfalse);
initialValues.put(KEY_GID, gid);
---------
Intent i = new Intent("data_inserted");
i.putExtra("date", date);
sendBroadcast(i);
---------
return mDb.insert(SQLITE_TABLE, null, initialValues);
}
After the change happened it will send an broadcast intent. To fetch this first register your Broadcast Receiver in your onCreate Method (uploaderClass or whatever). This will look like this:
registerReceiver(Updated, new IntentFilter("data_inserted"));
And this Method to handle the following actions!
private final BroadcastReceiver Updated= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doSomething(); // Data was inserted upload it
}
};
EDIT :
To fetch only new items from database, I sign new products. I have a colum "info" which can contain Strings like "upload", "update" or "delete". Then i fetch all Items from the database which contain these special strings and upload them. After that i set them to null or an empty String. Whatever you wish. Hope i explained it not to complicated :)
You can create your own database listener whenever something is updated to the database it will fetch the information and send to the network. I think will clear some idea for implementing this thing.
Now we can use Room to achieve this.
database.getInvalidationTracker().addObserver(new InvalidationTracker.Observer(tableToObserve) {
#Override
public void onInvalidated(#NonNull Set<String> tables) {
}
});
database is an instance of RoomDatabase, which is the base class for all Room databases. All classes that are annotated with "#Database" (which is the way to use the Room Libary) must extend this class.
In the creator method of InvalidationTracker.Observer, you can pass in an array of String (or in the form of varargs) to indicate the tables you'd like to observe, if any update happen to those tables, the callback method onInvalidated is invoked.
Some links to refer to:
https://developer.android.com/reference/android/arch/persistence/room/InvalidationTracker.html#addObserver(android.arch.persistence.room.InvalidationTracker.Observer)
hope so this will help you
private Handler h;
// in create time
h = new Handler();
// call where you want
h.postDelayed(myRunnable2, 1000); // after 1000 millisecond this function call automatically
// this function
private Runnable myRunnable2 = new Runnable() {
public void run() {
// do some thing
h.postDelayed(myRunnable2, 1000);
}
};