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.
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 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.
I'm trying to solve a (hypothetical) concurrency problem in my Android app that uses ORMLite for the database management.
In particular, I have a ContentService class that manages the database, here's some code (simplified to understand the problem):
public ContentManagerImpl(Context context) {
mContext = context;
configure();
// Once configured, get the DatabaseHelper
DatabaseManager.configure(context);
}
private void configure() {
// If needed, unzip a folder with an sqlite file
// representing the database and saves it on device
}
#Override
public void checkForUpdate() {
// Checks if new database version is available
new CheckForUpdateTask(CheckForUpdateTask.CheckForUpdateCallback() {
#Override
public void onCheckForUpdateFinished(boolean updateNeeded) {
if (updateNeeded) {
update();
}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
public void update() {
// Downloads the new database zip, unzip the folder and sava an sqlite file
// representing the database
new UpdateTask(mContext, mMetaData, new UpdateTask.UpdateCallback() {
#Override
public void onUpdateFinished() {
DatabaseManager.refresh();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
This class is created in the onCreate method of my Android Application class.
Since in the app I have only one Activity, the checkForUpdate method is called every time the onResume of that activity is called.
The problem is, sometimes when there's an update needed and the app is resumed (or even when first launched) I get an exception on a database query performed inside the DatabaseManager.configure() and DatabaseManager.refresh() methods.
That's because I try to get the first element of a single-row table but the query returns an empty list. It seems that the database is not ready, or that someone is still writing on it. I've also tried some lock mechanism but without luck.
So, since is the main (UI) thread that overwrites the database file, my questions are:
Can it be a problem to access the database from multiple threads/tasks?
Can I perform database queries on a separated thread if I write the db file on the UI thread?
Thank you all for your support.
This question is a follow-up question from: Organize Android Realm data in lists
Due to the data returned by the API we use, it's slightly impossible to do an actual query on the realm database. Instead I'm wrapping my ordered data in a RealmList and adding a #PrimaryKey public String id; to it.
So our realm data looks like:
public class ListPhoto extends RealmObject {
#PrimaryKey public String id;
public RealmList<Photo> list; // Photo contains String/int/boolean
}
which makes easy to write to and read from the Realm DB by simply using the API endpoint as the id.
So a typical query on it looks like:
realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
This creates a slightly overhead of listening/subscribing to data because now I need to check listUser.isLoaded() use ListUser to addChangeListener/removeChangeListener and ListUser.list as an actual data on my adapter.
So my question is:
Is there a way I can query this realm to receive a RealmResults<Photo>. That way I could easily use this data in RealmRecyclerViewAdapter and use listeners directly on it.
Edit: to further clarify, I would like something like the following (I know this doesn't compile, it's just a pseudo-code on what I would like to achieve).
realm
.where(ListPhoto.class)
.equalTo("id", id)
.findFirstAsync() // get a results of that photo list
.where(Photo.class)
.getField("list")
.findAllAsync(); // get the field "list" into a `RealmResults<Photo>`
edit final code: considering it's not possible ATM to do it directly on queries, my final solution was to simply have an adapter that checks data and subscribe if needed. Code below:
public abstract class RealmAdapter
<T extends RealmModel,
VH extends RecyclerView.ViewHolder>
extends RealmRecyclerViewAdapter<T, VH>
implements RealmChangeListener<RealmModel> {
public RealmAdapter(Context context, OrderedRealmCollection data, RealmObject realmObject) {
super(context, data, true);
if (data == null) {
realmObject.addChangeListener(this);
}
}
#Override public void onChange(RealmModel element) {
RealmList list = null;
try {
// accessing the `getter` from the generated class
// because it can be list of Photo, User, Album, Comment, etc
// but the field name will always be `list` so the generated will always be realmGet$list
list = (RealmList) element.getClass().getMethod("realmGet$list").invoke(element);
} catch (Exception e) {
e.printStackTrace();
}
if (list != null) {
((RealmObject) element).removeChangeListener(this);
updateData(list);
}
}
}
First you query the ListPhoto, because it's async you have to register a listener for the results. Then in that listener you can query the result to get a RealmResult.
Something like this
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
RealmResults<Photo> photos = listPhoto.getList().where().findAll();
// do stuff with your photo results here.
// unregister the listener.
listPhoto.removeChangeListeners();
}
});
Note that you can actually query a RealmList. That's why we can call listPhoto.getList().where(). The where() just means "return all".
I cannot test it because I don't have your code. You may need to cast the element with ((ListPhoto) element).
I know you said you're not considering the option of using the synchronous API, but I still think it's worth noting that your problem would be solved like so:
RealmResults<Photo> results = realm.where(ListPhoto.class).equalTo("id", id).findFirst()
.getList().where().findAll();
EDIT: To be completely informative though, I cite the docs:
findFirstAsync
public E findFirstAsync()
Similar to findFirst() but runs asynchronously on a worker thread This method is only available from a Looper thread.
Returns: immediately an empty RealmObject.
Trying to access any field on the returned object before it is loaded
will throw an IllegalStateException.
Use RealmObject.isLoaded() to check if the object is fully loaded
or register a listener RealmObject.addChangeListener(io.realm.RealmChangeListener<E>) to be
notified when the query completes.
If no RealmObject was found after
the query completed, the returned RealmObject will have
RealmObject.isLoaded() set to true and RealmObject.isValid() set to
false.
So technically yes, you need to do the following:
private OrderedRealmCollection<Photo> photos = null;
//...
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<ListPhoto>() {
#Override
public void onChange(ListPhoto element) {
if(element.isValid()) {
realmRecyclerViewAdapter.updateData(element.list);
}
listPhoto.removeChangeListeners();
}
}
I have a ListView which I need to populate using a background thread. The list needs to update as each item is retrieved. Below is a very simplified example of how I implement this.
public class DownloadTask extends AsyncTask <MyUserObject, Integer, String>
{
#Override
protected MyUserObject doInBackground(MyUserObject... myUserObj)
{
MyUserObject muo = null;
int nCount = myUserObj.length;
if( nCount > 0 )
muo = myUserObj[0];
muo.DownloadStuff();
return muo.getUserName();
}
protected void onPostExecute(String userName)
{
adapter.names.add(userName);
adapter.notifyDataSetChanged();
}
}
public class MyAdapterClass extends BaseAdapter
{
private ArrayList<String>names;
public MyAdapterClass(Context context)
{
names = new ArrayList<String>();
}
public fillList()
{
for( int i=0; i<users.length; i++ )
{
DownloadTask task = new DownloadTask();
task.execute(users[i]);
}
}
In the above example, 'adapter' is an object of type MyAdapterClass, and its fillList() method is what launches the threads. Calling notifyDataSetChanged() in onPostExecute() is what updates my ListView as data arrives.
The problem is, that I am accessing my sqlite database in "DownloadStuff()' which is called in 'doInBackground', and having multiple threads accessing the DB causes it to crash. (If I comment out all DB activities in here, then it runs fine). Below is how I try to workaround this problem, however it still crashes. Any advice on how I can have my ListView update as data is retrieved from a background thread?
Semaphore semaphore = new Semaphore(1, true);
public synchronized void DownloadStuff()
{
semaphore.acquire(1);
// ... DB operations ... //
semaphore.release(1);
}
I think your approach is wrong from it's beginning. Why do you want to start separate AsyncTask for each item you have to add to your adapter. Use onProgressUpdate to notify the gui for newly added items in the adapter. In this case you want have concurrent access to your db.
I'm not sure (because I'm really tired) but I think your ot using you synchronysed correctly.
you create a different instance of MyUserObject each time you do a async task, this means you never actually call Downloadstuff on the same instance hence no conflict, but on the other hand your database is unique being called by multiple MyUserObject hence conflict.
what you want to do is have the same instance of muo in all your async task, this way they all call downloadstuff on the same instance and then synchronized will work preventing multiple access.
you also don't need the semaphoe here.
edit:
Mojo Risin answer is also very good, if you can save yourself the trouble by centralizing all you async tasks into one you should(less concurrent threads running around you have the better)