I have implemented a SyncAdapter, AccountManager and private ContentProvider along the lines of the SimpleSyncAdapter sample project in the SDK. It is all working well.
Now I want to show a message to the user when new rows have been downloaded from the remote server that have a specific flag set. I need a callback from the SyncAdapter when a Sync has finished so I can do the query and display the message from an activity. I have seen a few questions on StackOverflow discussing this but none with a good answer.
How does one listen for progress from Android SyncAdapter? says that the SyncStatusObserver is useless. User mobibob suggests using a ResultReceiver to respond back to the UI from the sync thread.
How to know when sync is finished? suggests using an Intent in your SyncService.
How to signal sync-complete to the Android SyncManager? suggests using the SyncResult. The example code linked to by maxpower47 uses the SyncResult class to report exceptions but not to actually report if a sync was successfully completed.
I just don't know which is the best option and I have not seen any example projects where any of these solutions are used.
I know this is an old question, but I was asking the same thing myself.
What I found out as a good solution, specially because I'm dealing with local data as you are, is to use the following method from ContentResolver:
registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
This register an observer class that get callback when data identified by a given content URI changes. But that can only happens if your ContentProvider send the notification. So for example, if you want to get notified on the ContentObserver above for all updates done on your database through a ContentProvider, your ContentProvider should implement update similar to this:
#Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
// code goes here
this.getContext().getContentResolver().notifyChange(uri, null);
return 0;
}
Using notifyForDescendents when you do a registerContentObserver can be very useful.
This is an old question but I did some research in the past days and there are not many exemples on syncAdapter handling network requests and notifying the UI.
First you should use Loaders with contentProvider to make your life easier. You don't need to register for content resolver anymore the Loader does it for you. So it means your UI gets notified for anything that goes into your content provider.
What if nothing changed ? everything was up to date or you had a network error.
You can listen to the status of your syncAdapter as the Google I/O
app does, search for mSyncStatusObserver in the BaseActivity
I had a look at the default android email app and they use a Singleton with callBacks.
You can BroadcastIntents or use an eventBus (square Otto for exemple) to notify your UI of any behaviour.
I like the last one better because it gives you more granularity on the events that happen in the syncAdapter.
We ran into a similar situation and wrote a static Listener interface to the SyncAdapter. The listener is the activity and performs necessary actions when the data is available (update UI). This also works when the sync-adapter is called by the system during autosync where this listener would be null and the sync process would mind its own business.
class SyncAdapter extends AbstractThreadedSyncAdapter {
protected static Listener uiListener = null;
public interface Listener {
public void onSync();
}
public static void setListener(Listener l) {
uiListener = l;
}
public static void clearListener() {
uiListener = null;
}
protected void broadcastSync() {
if (uiListener != null)
uiListener.onSync();
}
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
// call broadcastSync();
}
Then in the Activity, implement SyncAdapter.Listener interface.
Related
I recently ran into discussion about usage of Subject, like this one here: https://github.com/JakeWharton/RxRelay/issues/7
I see a lot of people saying that Subject should be avoided and some people even say any usage of Subject is inherently a bad practice. While I agree on the theoretical level that Subject can be and should be avoided, I can hardly get rid of subjects in real practices. It seems impractical, or even impossible to do so.
Imagine a simple theoretical weather app that has just two things:
a view that displays current weather information
a refresh button which re-fetch the weather information from the server.
(Let's assume for simplicity that the app does not show the data at initial launch, but waits for the users to press refresh button at least once.)
Then you can think of a view model design like this:
ViewModel
interface IWeatherViewModel {
// Provides weather data
Flowable<WeatherData> getWeatherDataToDisplay();
// Lets view to refresh
void refresh();
}
If I use Subject then IWeatherViewModel can be implemented like this:
class WeatherViewModel implements IWeatherViewModel {
private final BehaviorProcessor<WeatherData> weatherData = BehaviorProcessor.create();
private final PublishProcessor<Boolean> eventRefresh = PublishProcessor.create();
WeatherViewModel() {
eventRefresh
.flatMapSingle(x -> getWeatherData())
.subscribe(weatherData);
}
// Provides weather data
public Flowable<WeatherData> getWeatherDataToDisplay() {
weatherData.hide();
}
// Lets view to refresh
public void refresh() {
eventRefresh.onNext(true);
}
private Single<WeatherData> getWeatherData() {
... // omitted for simplicity
}
}
The idea is to have a PublishProcessor that emits refresh event whenever refresh() is called which is then propagated to a BehaviorSubject. All subscribers that observe getWeatherDataToDisplay() will be notified once getWeatherData() is successful.
However I find it difficult to implement the same thing without Subject.
The app needs to propagate refresh() call to stream. I might be able to replace PublishProcessor using Flowable.create() but it doesn't look clean at all, the best I could do is:
private FlowableEmitter emitter;
private final Flowable<Boolean> eventRefresh = Flowable.create(emitter -> {
this.emitter = emitter;
}, BackpressureStrategy.BUFFER);
public void refresh() {
emitter.onNext(true);
}
Now suddenly I have to have a new instance variable that I cannot make final..
Also I am not able to find any operator that can effectively replace BehaviorProcessor, nor any hot observable that emits the latest item immediately on subscription. This behavior is necessary because the view should be able to detach and re-attach seamlessly, just like LiveData.
If you see any improvement that can be made, or have a different approach to the problem, please share your thougts.
Let me to start explain my problem. There is repository with some explanations, but there are no methods how to get collection or json file from Meteor server(only insert). Also author did not explain properly methods onDataChanged, onDataAdded etc.
public class Login extends Activity implements MeteorCallback{
public static Meteor mMeteor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mMeteor = new Meteor(this, "some_socket_it_doesn't_matter");
mMeteor.setCallback(this);
}
}
public class ListOfElements extends ListFragment implements MeteorCallback{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String subscriptionId = Login.mMeteor.subscribe("notifications");
Log.d("Log", subscriptionId);
}
}
I didn't understand how i have to use subscription or how to get collection from server. Why there are only insert methods in github repository and no get? I really have no idea how make the code to get collection, use subscribe and so on. There are no any understandable explanations in the network. Please, can you help me with this by explaining how to realize getting, subscribing in this code.
There are two special things about Meteor: It works asynchronously and it has been designed specifically for real-time applications. Thus it has a few different concepts for retrieving data and for some other tasks.
In a synchronous application, you would just call insert(...) and immediately get the method's return value, e.g. a boolean value for success/error or a numeric value for the number of rows that have been inserted.
You would call get(...) and immediately receive a collection of rows as the method's return value.
But in Meteor, everything is asynchronous. This means that you get the results not immediately, but a few (milli)seconds later, in a callback method.
When you call insert(...), this is not so important, as you have noticed. You just call this method and often forget about the result, i.e. you don't wait and check for the result because insertions are usually successful. But this method is still asynchronous and you could (and sometimes should) listen for the result which will arrive a few (milli)seconds later, again.
When you want to call get(...), this would be possible in theory, with the important point again being that it's asynchronous. So you would say "get me all chat messages from the last 5 minutes". There would be no result or return value, as usual, but the result would arrive a short time later, asynchronously, in a callback method that you define. This is what onDataAdded(...), onDataChanged(...) and onDataRemoved(...) are for.
Now it's not clear, yet, why you can't call get(...) and wait for data to arrive in those methods.
The answer to that question is Meteor being designed for real-time applications. This is why you can't say "get me all chat messages from the last 5 minutes". Instead, you have to say "I want to subscribe to all chat messages from the last 5 minutes and always be updated about changes".
So, in Meteor, you subscribe to data sets instead of requesting them via get(...).
All in all, this means the following:
If you want to get some messages, you subscribe to your data set that holds those messages.
When the initial rows are sent (!) and whenever new rows are added to the collection, you receive those in your onDataAdded(...) callback. When rows are modified, you receive those changes in your onDataChanged(...) callback. And, finally, when rows are deleted, you are informed about those deletions in your onDataRemoved(...) callback.
When you don't want to get updates for your data set anymore, you unsubscribe from that set. This is optional.
With the Android-DDP library in your Android application, it translates to the following:
final String subscriptionId = mMeteor.subscribe("chats");
public void onDataAdded(String collection, String docID, String json) { ... }
mMeteor.unsubscribe(subscriptionId);
As you can see, what you have to learn is really Meteor and not the library Android-DDP. Meteor has some new concepts that one has to understand. But when you know how Meteor works, translating those things to Android-DDP is really simple and only a matter of looking up the method names.
As I understand from the docs, one SyncAdapter defined in a SyncService is limited to receive only one ContentProvider authority to work on.
But, at the same time, it has access to ContentResolver which it allows to run queries on other ContentProviders as well. I don't understand this particular design concept if a developer is needed to provide a single content authority to SyncAdapter and nonetheless she is able to do whatever she wants on whatever ContentProvider she has access to. My question is: What are the consequences of ignoring onPerformSync's parameters: String authority and ContentProviderClient provider and going with pure ContentResolver?
My application's (actually its SyncService) idea is simple: query a calendar server (OwnCloud in my case) to get not only events (synced with com.android.calendar) but also VTODOS, which are then distributed between various task management apps I can get source code and/or ContentProviderContract. I also thought of my own "Hub" ContentProvider, which has basic VTODO/Task structure, and is the only one compared to the server. It should be able to sync 2-way with different content providers of task management apps and then it syncs with the server.
I have read using ContentProviderClient vs ContentResolver to access content provider and I think I understand the difference. I'm now puzzled why there is so strong suggestion from android SDK to use a single ContentProvider in a single SyncAdapter and yet you are allowed to use ContentResolver to bypass that limitation.
I spent all day figuring this out and searched hundreds of SO/Google resources on the matter (some of them multiple times). I have also seen questions regarding using one SyncAdapter to sync multiple ContentProviders, but none of the answers were any close to suggesting using ContentResolver instead.
There is no special limitation on ContentResolver's API when used from the context of SyncAdapter. IMHO, the only reason why the framework passes ContentProviderClient and authority to onPerformSync() is convenience and kind of a hint to developers as to how SyncAdapter intended work.
This fact is easily seen in the source code for AbstractThreadedSyncAdapter.SyncThread - the ContentProviderClient passed to onPerformSync() is obtained in a standard fashion:
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Trace this sync instance. Note, conceptually this should be in
// SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
// threads in order to track overlapping operations, so we'll do it here for now.
Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
SyncResult syncResult = new SyncResult();
ContentProviderClient provider = null;
try {
if (isCanceled()) {
return;
}
provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
if (provider != null) {
AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
mAuthority, provider, syncResult);
} else {
syncResult.databaseError = true;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
if (provider != null) {
provider.release();
}
if (!isCanceled()) {
mSyncContext.onFinished(syncResult);
}
// synchronize so that the assignment will be seen by other threads
// that also synchronize accesses to mSyncThreads
synchronized (mSyncThreadLock) {
mSyncThreads.remove(mThreadsKey);
}
}
}
Therefore, the bootom line: you can use ContentResolver in your SyncAdapter as you wish - just call getContext().getContentResolver() and access any exported ContentProvider.
I have problem when implementing AsyncTask. I have to rotate my phone in order to get a recent information. Below is my class:
GamerObject gamer;
….
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ip = "134.188.204.155";
// Set the name of the gamer
gamername = (TextView) findViewById(R.id.gamer_name);
// Set the gamerstatus:
gamerstatus = (TextView) findViewById(R.id.tvgamer_status_msg);
// set the job status
jobstatus = (TextView) findViewById(R.id.tvJob_status_msg);
new Operation().execute();
}
private class Operation extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
gamer= new GamerObject(ip);
gamer.UpdateAllData();
}
#Override
protected void onPostExecute(Void result) {
updateUI();
}
}
private void updateUI() {
gamer.updateAllData();
// Set the name of the gamer
TextView gamername = (TextView) findViewById(R.id.gamer_name );
gamername.setText(gamer.gamername);
gamername = gamer.gamername ;
// Set the gamer status:
…
// set the job status
…
}
Before I was using a Intent for Refresh the Interface, but now I want to try using AsyncTask so that it can compatible with Android version 4. Anybody knows how to get the information updated without rotating the phone ?
EDIT:
If I'm not wrong, Why my UI didn't refresh if there is new value, it is because new Operation().execute(); only use once in onCreate. AsyncTask will be executed if onCreate has been called, which means every time I rotate my phone, it will go to onCreate . Anybody knows how to keep the AsyncTask executed?
AsyncTask will only be executed once, so whenever system calls onCreate on your activity dueto some lifecycle event, asyncTask will be executed.
One, simple but naive approach, would be to create a new Thread and use Handler to update your UI. Some more information can be found eg. here and of course in Android doc.
Better approach, but more complicated would be to use Loader and LoaderCallback along with ContentProvider as #metter mentioned. This will require implementing you own ContentProvider and force you to add more "abstraction layers" to your app but will allow to separate network base code and ui code.
So this is as always tough decision to make either use "simple" but ugle solution with threads or "harder" but elegant solution with ContentProvier
I want to get the inofrmation updated automatically without have to rotating the phone.
Doing that requires some more work. Unfortunately, we can't see what your Async task actually does. If it is reading data from a database and you wan't your UI to be informed about any changes to the database, then your content resolver could call it's notifyChange and your Activity would listen to these changes and then call the async task again. For that, you would use a Content Observer. However, if your task is downloading data from the web, then there are two methods to get informed if the data online changed. One is called polling and means that you periodically connect and check the server. You should never do that on a mobile device due to limitations in battery, performance and data traffic. The other is called pushing and requires you to set up some infrastructure.
I hope that helps you.
I have severals URLs I need to get data from, this should happen in order, one by one. The amount of data returned by requesting those URLs is relatively big. I need to be able to reschedule particular downloads which failed.
What is the best way to go? Shall I use IntentService, Loaders or something else?
Additional note: I would need not only to download, but also post process the data (create tables in db, fill it with data, etc). So DownloadManger can't be of help here.
I would use an IntentService.
It has a number of advantages that are suitable for your needs, including being able to download the data without your application running and supporting automatic restart of the service using setIntentRedelivery().
You can set a number of identifiers for the particular job, you need to perform using Intent extras, and you can keep track of the progress using SharedPreferences - that way you can also resume the work if it's been cancelled previously.
The easiest way is probably to use the system DownloadManager http://developer.android.com/reference/android/app/DownloadManager.html
(answering from my phone, so please excuse the lack of formatting)
I would suggest a service for this. Having service resolves many problems
It would allow reporting of progress asynchronously to the application so you can enable or disable a specific gui in application based on the download status of data
It will allow you to continue the download even if the user switches to other application or closes the application.
Will allow you to establish independent communication with server to prioritize downloads without user interaction.
Try a WakefulIntentService for creating a long-running job that uses wakelocks to keep your task alive and running https://github.com/commonsguy/cwac-wakeful .
Also, if your whole app process is getting killed, you may want to look into persisting the task queue to disk, using something like Tape, from Square
I think the way to go is loading urls in an array, then starting an AsyncTask, returning a boolean to onPostExecute indicating if the operation has success or not. then, keeping a global int index, you can run the AsyncTask with the next index if success, or the same index otherwise. Here is a pseudocode
private int index=0;
//this array must be loaded with urls
private ArrayList<String> urlsArray;
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
class MyDownloaderAsyncTask extends AsyncTask<String,String,Boolean>{
#Override
doInBackground(String... input){
//downlaod my data is the function which download data and return a boolean
return downloadMyData();
}
#Override
onPostExecute(Boolean result){
if(result)
new MyDownloaderAsyncTask().execute(urlsArray.get(++index));
else
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
}
}
hope this help
I have just completed an open source library that can do exactly what you need. Using droidQuery, you can do something like this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.type("GET")
.dataType("JSON")
.context(this)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
//since dataType is JSON, params[0] is a JSONObject
JSONObject obj = (JSONObject) params[0];
//TODO handle data
//TODO start the next ajax task
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = params[0];
//TODO adjust error.options before retry:
$.ajax(error.request, error.options);
}
}));
You can specify other data types, which will return different object types, such as JSONObject, String, Document, etc.
Similar to #Murtuza Kabul I'd say use a service, but it's a little complicated than that. We have a similar situation related to constant internet access and updates, although ours places greater focus on keeping the service running. I'll try to highlight the main features without drowning you in too much detail (and code is owned by the company ;) )
android.permission.RECEIVE_BOOT_COMPLETED permission and a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED to poke the service awake.
Don't link the service to the Activity, you want it running all the time. eg we call context.startService(new Intent(context.getApplicationContext(), OurService.class))
The service class is just a simple class which registers and calls an OurServiceHandler (as in our case we fire off repeated checks and the Handler manages the 'ticks')
We have an OurServiceRunnable which is a singleton which is checked and called by the Handler for each test. It protects against overlapping updates. It delegates to an OurServiceWorker to do the actual lifting.
Sounds heavy handed, but you want to ensure that the service is always running, always ticking (via the Handler) but only running a single check at a time. You're also going to run into database issue if you use the standard SqlLite DbHelper paradigm, as you can't open the DB on multiple threads and you definitely want the internet access off the main thread. Our hack was a java.util.concurrent.locks.ReentrantLock protecting access to the DB, but you could probably keep DB access on the UI thread and pass DB operations via the Handler.
Beyond this it's just a matter of keeping the downloads atomic in terms of "get task, download task, complete task" or enabling it to pick up from a failed state eg downloaded OK, attempt to complete.
You should take a look at the volley library :
http://www.javacodegeeks.com/2013/06/android-volley-library-example.html
There is also an interesting video of the author that took place at google io 2013 :
http://www.youtube.com/watch?v=yhv8l9F44qo
Mainly because it eases the process of managing a lot of these fastidious tasks that are connection checking, connection interruption, queue management, retry, resume, etc.
Quoting from the javacodegeeks "Advantages of using Volley :
Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
Volley provides transparent disk and memory caching.
Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
Volley provides powerful customization abilities.
Volley provides Debugging and tracing tools"
Update from dennisdrew :
For large file, better use a variant of volley which authorize using another http client implementation. This link gives more details :
The volley article about this modification :
http://ogrelab.ikratko.com/android-volley-examples-samples-and-demos/
The github file detail :
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/ExtHttpClientStack.java
public class FetchDataFromDBThread implements Runnable {
/*
* Defines the code to run for this task.
*/
#Override
public void run() {
// Moves the current Thread into the background
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
FetchDataFromDB();
}
}