somehow I don't understand the working concept of the query parameter CALLER_IS_SYNCADAPTER. Its default value is false, if set, the DIRTY flag is not automatically set. So what does it actually mean? Out from my understanding, each change on a contact results in setting the dirty flag to 1. After a sync adapter finished the job, using insert/update/delete with the CALLER_IS_SYNCADAPTER the inserted/updated and deleted records should have a dirty flag of 0, is that right?
However if I invoke queries with that optional parameter, the entries remain with the flag 1.
Is there something else I have to do, or is my understanding how it should work wrong? Or is there something to tell the system the sync has been finished successfully to set the flags?
Does anybody have a sample or some advices for further reading?
CALLER_IS_SYNCADAPTER doesn't necessarily affect what's stored in the database row, it depends on the command performed. It shouldn't have an effect on queries. Do not use it from a user application on the device.
Now... Why does it exist?
It is provided to help with notifyChange() / ContentObservers / ContentResolver / Syncadapter integration. There are two use cases for changing a row in the database.
Local user edits from an application.
Changes come from the network (via SyncAdapter)
Either change requires the UI to update, if it's onscreen. Therefore ContentResolver.notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) gets called. This updates ContentObservers and tells them to go fetch the newest data from the ContentProvider DB. That last parameter in the call is your clue.
ContentResolver itself is a ContentObserver. When it sees the database change, it considers starting up your SyncAdapter to push the change up to the network. This is great in case 1. In case 2, it's redundant. The change came from the network, there's no reason at all to start up a sync to send the change back.
Calendar.CALLER_IS_SYNCADAPTER is a cue used within the update() performed by the SyncAdapter. When it's true, ContentProvider sets syncToNetwork as false, ensuring a redundant second sync is not performed
A second example is as veljko mentioned. The cleanest way to delete a thing from the server is to set the delete flag, and then perform a sync. When the CALLER_IS_SYNCADAPTER flag is false (user app) a call to delete() sets the flag. When the flag is true (sync is happening), a call to delete() pushes the deletion up to the server and removes the row from the local DB. There's only one delete() call, this flag allows the ContentProvider to know which task it's supposed to do.
You can add to your existing Uri:
myUri=calendarUri.buildUpon().appendQueryParameter(Calendar.CALLER_IS_SYNCADAPTER, "true").build();
Here is from Javadoc:
/**
* An optional insert, update or delete URI parameter that allows the caller
* to specify that it is a sync adapter. The default value is false. If true
* the dirty flag is not automatically set and the "syncToNetwork" parameter
* is set to false when calling
* {#link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
*/
.
The invocation of resolver.delete(...), does not immediately delete a raw contacts row. Instead, it sets the DELETED flag on the raw contact and removes the raw contact from its aggregate contact. The sync adapter then deletes the raw contact from the server and finalizes phone-side deletion by calling resolver.delete(...) again and passing the CALLER_IS_SYNCADAPTER query parameter.
Related
I have a node in Firebase Realtime Database which saves a boolean value true or false along with some more attributes. If and only if this key is false, then I allow the user to perform write operations on the same node in database.
When user has did the task, this value is again set back to false. This is like the critical section concept of the operating systems i.e., only one user can perform a task at a time.
It works as I intended, but the issue I am facing is if user has changed this value to true, and but due to bad network or something he/she is not writing on the database. Now, no one can write on that node on database.
I would like to add a time-interval functionality, if user has not performed any write operation for some interval, say 10 minutes and the boolean is true. Then, I would like to set it to false.
I know that Firebase Cloud Function triggers only on database write, update and create operations. Please suggest me something to handle this issue or some other ways by which I can perform it. There is no code snippet regarding this functionality anywhere. I have looked up various resources on internet, brainstormed myself, I could get nothing.
This might not be perfect solution, but it is a workaround.
At the place, where you are allowing data write, probably through a conditional check; you can simply add another condition by using OR operator.
Simply allow data write when (that Boolean value is false) or (boolean value is true but the node's last updation timestamp is more than current timestamp by your time of interval).
It should me something like this :
if(!dataSnapshot.getBoolean || (dataSnapshot.getBoolean && currentTimestamp - dataSnapshot.getTimestamp >= YOUR_TIME_INTERVAL)){
//Your Logic Here
}
I have a question about Firebase Database. After data has been changed , does the listener give you whole data again or just the recently added data?
The documentation says:
You can use the onDataChange() method to read a static snapshot of the
contents at a given path, as they existed at the time of the event.
This method is triggered once when the listener is attached and again
every time the data, including children, changes. The event callback
is passed a snapshot containing all data at that location, including
child data. If there is no data, the snapshot returned is null.
So also the unchanged parts of the data are returned.
This just means that you should use as specific and narrowed-down listeners as possible, as the documentation also suggests:
Important: The onDataChange() method is called every time data is
changed at the specified database reference, including changes to
children. To limit the size of your snapshots, attach only at the
highest level needed for watching changes. For example, attaching a
listener to the root of your database is not recommended.
I want registered callback method using sqlite trigger
for example,
public void printLog(){
Log.i("TAG","1 row added");
}
this method calling after insert any row in sqlite.
Is it possible?
How to do that?
SQLite provides Data Change Notification Callbacks. I don't think that Android exposes them directly but it does have for example CursorAdapter which provides some change notifications.
You can use also use the getContentResolver().registerContentObserver but unfortunately it doesn't tell you what kind of change was made, it could be a delete, insert or update.
If you control the ContentProvider that interfaces with the DB then you could fire an Intent or use getContentResolver().notifyChange to send a special Uri notification that identifies both the table and action. An example Uri you could notify with might be: content://my-authority/change/table-name/insert
But even then you don't know exactly which rows were effected by the change.
Seems like triggers that write to a change log table will guarantee you hear about all changes regardless of where they came from, and you can know the exact id and action that occurred. Unfortunately it means slower inserts/updates/deletes and it means you probably need a Service of some kind to process and delete changes.
I'd love to hear if these is some better solution out there!
You can set content observer this link will help Receives call backs for changes to content http://developer.android.com/reference/android/database/ContentObserver.html
I have a content provider which can query multiple other content providers (via content resolver), and does some merging of contact data.
Basically I have extended asyncTask and handle the the data. In my main UI thread I do the following
cancelAllExistingTask();
proiverTest1 = new ProviderTask(getActivity, MyActivity.this.callBack).execute(SearchString, elementId1);
proiverTest2 = new ProviderTask(getActivity, MyActivity.this.callBack).execute(SearchString, elementId2);
proiverTest3 = new ProviderTask(getActivity, MyActivity.this.callBack).execute(SearchString, elementId3);
proiverTest4 = new ProviderTask(getActivity, MyActivity.this.callBack).execute(SearchString, elementId4);
So I create 4 instances of my ProviderTask, as part of the ProviderTask constructor, I pass in an interface which is implemented in my instance of callBack class.
class CallBackClass implements MyCallBackIF{}
The ProviderTask during doInBackground fires off a ContentResolver.query() to ONE provider. The same provider is used by all 4 Tasks. But based on the elementId it is returned to the interface via onPostExecute() w/ the elementId of the cursor array it should be placed into (see ** below).
The contentProvider parses the URI that is passed in, and based on the URI goes and queries 1 other ContentProvider for data. This could be local data, or remote from a server. Then depending on the source, it may merge it with other data (local), and provide back a new cursor w/ merged data. The individual Content Provider -> Content Resolver -> Content Provider -> Content Resolver is quite fast. And somewhat useful for providing search aggregation across a few different apps we have have. The provider does actually spawn an asyncTask to load the remote content provider data, and there is a synchronized block that waits on it to finish returning data before itself returning to the Activity. Part of the reason for this is that I could pass in a uri that has multiple providers to search & merge where it fires off more than one content resolver query into its own Merge Cursor. (But right now it is a Merge Cursor w/ 1 element that does run on an Async Task).
**
What I am doing is using a merge cursor & Cursor[] to update a list view based on the merged data from the various different providers. You may be asking, why not allow the content provider just do that work for us? We tried. It did not seem to work for us, but open to suggestions.
So as it stands if our MergeProvider Queries - ContentProvider1, ContentProvider2, ContentProvider3, ContentProvider4 and say ContentProvider3 also has to query ContentProvider 1 to merge some data. ContentProvider 3 & 4 are remote (server based)
W/ a predictive search we want search results that return back fastest to show up first. And the others to trickle in as they come back, if a new letter is typed, we want to dump the entire result set, and wait for a new query.. This is what is happening, and it seems like we are being blocked somewhere (we have tried upping thread priority of the AsyncTask, we have ExecuteExecutor w/ our own Executor & pool (upping the async task max of 10 to 100) w/ no results).
So someone types in a letter
'a' - Content Provider 1, and Content Provider 2, return in say 0.050 seconds. Content Provider 4 returns in say 0.100 seconds. And Content Provider 3 returns in 5.00 seconds. (The 5.00 delay is related to a test server we are testing on, but it has exposed the problem we are seeing w/ blocking).
now if they continue to type, and the string shows
'albert'
It may have fired off a new AsyncTask for 'al' which some return quickly, and others do not.. Say the Provider 3 is still waiting on a response.. Our code discards results if the predictive search has changed by the time the results come back. (which is fine).
so then it fires off another round of AsyncTasks 'albert'. Now remember Provider 3 is still off in it's 5 second response.
We added some logging in both the AsyncTask and in the Calling method (handler).
What we are seeing is the AsyncTask getting created, but we are not seeing doInBackground() get started until SearchProvider3 returns results (and they are discarded). I am quite confused as to why this is happening. But it is basically blocking other AsyncTask objects. Not sure what would cause doInBackground() to not be called until another AsyncTask came back unless it was because of the max limit of 10 AsyncTasks? W/ our own implementation of ThreadPoolExecutor (and even creating two different instances of ThreadPoolExecutor), we still see the same issue.
It is VERY visible if in the query method of our Provider 3, we add a thread.sleep(60000). Basically it looks like maybe 5 Async tasks get called before they start blocking. Our goal was to get local matching results back faster independent of other long running tasks. This would be more apparent on a slow (3g) network.
Maybe we should not be using Async tasks for this, and just using runnables?
Thanks.
Rather than having our app implement the ContentResolvers and all the queries, we implemented it as part of a content provider, and dispatched the searches via IntentService.
We grab a single cursor to a temp table, then w/ the Service Intent, it fires off all the other content resolver queries, and populates the underlying temp table. If it takes a bit of time on one of the intent services, the user does not really notice as the others populate it quite quickly..
Seems to have resolved our issue.
I need to recognize first launch of my application or activity.
At this time I need to get some information from server create local database and save info to it. What is the best way to do this?
Create any preferences for example FirstLaunch and set true \ false to it.
Check whether my database exists or not.
Something else?
PS. All server calls must be into one transaction. Ormlite supports transactions?
Thanks.
For the "create database at first run"-purpose, you should use an SQLiteOpenHelper, which offers you the onCreate()-method that is called when:
[...] the database is created for the first time.
The Database-file itself will be created for you (you don't have to do this manually). In this method, you can then perform actions like populating your database with standard entry's.
If you want to populate the database with informations you get from your server, there might be a problem when there is no Internet-connection available.
In this case, I would check if there is a connection available:
If there is, get your informations.
If not, show a Toast or some other notification to inform the user.
To determine if your Database has be populated with the standard entry's, you can use the database-version which is also provided by the SQLiteDatabase-class:
When you first create your Database-object, you call
SQLiteOpenHelpers constructor and pass it 0 as the Database
version.
If you successfully populated your database, you use
setVersion()-method to alter it to 1.
Later in the onOpen()-method, which is called when the
database is opened, you can check if the database was populated by
using the getVersion()-method.
If it is populated, call the super-method to open it.
If not, try populating it.
Further more, the getReadableDatabase() / getWritableDatabase()-methods should be called off the main-thread anyways because:
Database upgrade may take a long time, you should not call this method
from the application main thread, including from
ContentProvider.onCreate().
So getting the informations from the Internet can take place in the onCreate() and in the onOpen()-method (if it wasn't successful at the first try). You can (for example) use a Service to do this.
If you want to solve this problem with database:
Create database with MyDatabasaVersion table and store your version in a single row, for example db_version default value is 0. First time when the application starts you check the db_version if 0 you need to start the syncronisation, after it is finishing set the db_version to 1.
The easiest way should be sharedpreferences. you can call it everywhere form the application context and you can put boolean values in it.
Here are all Android storages.
you should try first option Create any preferences for example FirstLaunch and set true \ false to it.