get notified when requestSync function completed - android

im trying to start the calendar sync programatically using this code
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
ContentResolver.requestSync(accounts[0], "com.android.calendar", bundle);
i want a way so i can know when sync complete so i can read data from the calendar
i tried doing this
while (ContentResolver.isSyncActive(accounts[0], "com.android.calendar")) {
System.out.println("looping: " + i);
}
readLocalCalendar();
readLocalEvents();
but the system exit the loop before the sync ends and i can still see the sync sign at the status bar, so any help so i can read calendar events after sync completle done ??
thanks

Another option would be to register a broadcast receiver to tell you when the sync is finished like this:
public class UpdateableActivity extends Activity {
public static final String ACTION_FINISHED_SYNC = "your.package.ACTION_FINISHED_SYNC";
private static IntentFilter syncIntentFilter = new IntentFilter(ACTION_FINISHED_SYNC);
private BroadcastReceiver syncBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update your views
}
};
#Override
protected void onResume() {
super.onResume();
// register for sync
registerReceiver(syncBroadcastReceiver, syncIntentFilter);
// do your resuming magic
}
#Override
protected void onPause() {
unregisterReceiver(syncBroadcastReceiver);
super.onPause();
}
}
Inside your SyncAdapter use this when done:
getContext().sendBroadcast(new Intent(UpdateableActivity.ACTION_FINISHED_SYNC));
You can also use this for when starting or updating the status of the sync ;)
Update: Updated the code to avoid leaks and making sure the activity is still active (onResume/onPause)

using the addStatusChangeListener actually worked for me .
here's a reference .
don't forget to add the needed permissions .

Use ContentResolver.addStatusChangeListener (int mask, SyncStatusObserver callback) to get notified of changes in sync status. docs
Please do not loop forever, its really bad design. Using the above method everything is asynchronous so you don't waste any cpu cycles.
You could also use ContentResolver.registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer) docs to get notified in changes on a specific URI (like the calendar's URI)

try an AsyncTask :
private class CustomTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
// TODO sync your calendar
}
protected void onProgressUpdate(Void... progress) {
//TODO display a spinner or something else to show progress
}
protected void onPostExecute(Void t){
//TODO what you want when doInBackground has finished
}
}
Good luck !

Related

Android IntentService onDestroy being called instead of onHandleIntent

Checked the log for it.. onDestroy() method gets called instead of onHandleIntent()
i am using two intent services, and have written similar code for both... but one runs all the time but for the second intentService(code attached)...sometimes it runs and sometimes it doesn't without changing anything in the whole project.
can anyone please help?
public class GetDataService extends IntentService {
private static final String TAG="GetDataService";
public GetDataService(){
super("GetDataService");
}
#Override
protected void onHandleIntent(Intent intent) {
GetDataTask task= new GetDataTask();
Log.d(TAG,intent.getStringExtra(GetDataTask.INSTA_TOKEN_URL));
task.execute(this,intent);
ApplicaitonsData.GetDataServiceRunning=true;
Log.d(TAG,"data service running status = "+ ApplicaitonsData.GetDataServiceRunning);
}
#Override
public void onDestroy() {
super.onDestroy();
ApplicaitonsData.GetDataServiceRunning=false;
Log.d(TAG,"data service running status = "+ApplicaitonsData.GetDataServiceRunning);
}
}
the task.execute() method in code had a if loop in it and the condition was false. so there wasn't anything for the IntentService to do..therefore it was destroying itself.

Communication between Android Services and Activities

I want to develop an Android App with three activities and two services.
The first Service, named WebClientService, calls a REST API every 30 seconds, using an Handler, and has to notify the active Activity with the result.
It also has to notify a second Service, named DatabaseService, in order to update a local DB.
The Database Service will be called just once onCreate of the activity (in case of app crash and restart) and just once at onRestart (in this way we have data to show in case there were connectivity issues). The activities will then keep themselves updated thanks to the WebClientService that notifies the "alive" activity every 30 seconds.
Questions are:
What's the best way to notify for an update both the active activity and the background DatabaseService?
My idea is to use sendBroadcast() within WebClientService and a BroadcastReceiver in every activity and within the DatabaseService, is it the right approach?
Should I use the same approach for the communication between AllMeetingRoomActivity and DatabaseService or should I use a Bound Service?
Thanks
UPDATE:
DatabaseService won't be a background service anymore but just a shared instance of the db layer between WebClientService and the activities.
So question now is: is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db?
Would that affect the performance too much?
Context:
Follows what I've implemented so far but using SettableFutures and thus needs to be re-implemented using Services and Broadcasts once I've clear how to make them communicate effectively:
public class MainActivity extends AppCompatActivity {
private TextView meetingsTextView;
private EditText mEdit, editSubject;
private final ConnectorInitializer clientInitializer = new ConnectorInitializer();
private AppConnector genericClient; // can use OutlookClient or a test client to talk with a mock server
#Override
protected void onCreate(Bundle savedInstanceState) {
// initializes client based on the settings in "config.json"
genericClient = clientInitializer.create(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
meetingsTextView = (TextView) findViewById(R.id.NowMeeting);
mEdit = (EditText)findViewById(R.id.editText);
editSubject = (EditText)findViewById(R.id.editSubject);
Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
Log.d("APP", "-- Logged in. --");
databaseConnector.synchronouslyGetBackupFromLocalDatabase() // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e("\n ~~~~>> logon \n", t.getMessage());
meetingsTextView.setText(R.string.Login_Failed);
}
});
}
/** At the moment the UI is not updated automatically every 30 seconds
* but manually using a refresh button
*/
public void getBookings(#SuppressWarnings("UnusedParameters") View view){
Log.d("APP", "Retrieve button clicked: "+(DateTime.now())+". Calling async getCalendar.");
meetingsTextView.setText(R.string.retrieving_events);
try{
Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<String>(){
#Override
public void onSuccess(final String resultCalendars) {
Log.d("APP", "Success. Result: "+resultCalendars);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("APP", "Calendars SUCCESSFULLY retrieved.");
String meetingsRetrieved = getString(R.string.calendar)+resultCalendars;
meetingsTextView.setText(meetingsRetrieved);
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
}
});
databaseConnector.asyncUpdateLocalDbWithResults(); // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e( "APP", "Calendar error. Cause: "+t.getLocalizedMessage() );
String retrieveError = "Retrieve error. \n\n\n"+t.getLocalizedMessage();
meetingsTextView.setText(retrieveError);
Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
}
});
}catch(Exception ex){
Log.e("APP","Something went wrong in your code. Cause:"+ex);
}
}
Best option ever:
Use LocalBroadcastManager. More reference here.
MyService.java:
private LocalBroadcastManager localBroadcastManager;
private final String SERVICE_RESULT = "com.service.result";
private final String SERVICE_MESSAGE = "com.service.message";
#Override
public void onCreate() {
super.onCreate();
// Other stuff
localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
Add below method in service, whenever you want to update data from service to Activity, call method by passing Arguments.
private void sendResult(String message) {
Intent intent = new Intent(SERVICE_RESULT);
if(message != null)
intent.putExtra(SERVICE_MESSAGE, message);
localBroadcastManager.sendBroadcast(intent);
}
HomeActivity.java:
private BroadcastReceiver broadcastReceiver;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_home);
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(MyService.SERVICE_MESSAGE);
// do something here.
}
};
}
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
new IntentFilter(MyService.SERVICE_RESULT));
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onStop();
}
Hope this will help you.
I think your approach is ok with BroadCastReceiver. However, BroadCastReceiver should be used for a global purpose (like communicating between 2 applications). If you intend to use BroadCastReceiver for your app only, I prefer using LocalBroadcastManager instead. Using LocalBroadcastManager is faster and more security when it can be caught only by your app.
There's another way to communicate between your activitys and your services is using EventBus. It will be much easier than using BroadCastReceiver (especially in passing data between them).
Update: About your update question:
is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db? --> Of course NO. You should let your activities update themselves when they need. When you update your local db, you should know that is there any changes or not. If there is any change, use LocalBroadcastmanager to notify your activity to update.
Would that affect the performance too much? --> Yes, that do. The db connection will take time to execute and it will block your UI in some cases. in that case, you should use a thread with ExecutorService for each execute (insert, update...). One more thing to consider is updating that frequently will drain your phone battery very, very fast.
You can bind the services to the activities and update your UI.
Or you can use libraries like Otto or EventBus to create a publisher/subscriber dependency and notify your activities everytime your services publish an update of information.
Use event bus for this communication. EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration.
There are a lot of them:
http://square.github.io/otto/
https://github.com/greenrobot/EventBus
This is an example of Otto usage:
Bus bus = new Bus();
bus.post(new AnswerAvailableEvent(42));
#Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow!
}
bus.register(this); // In order to receive events, a class instance needs to register with the bus.
To post from any thread (main or background), in you case a Service and receive events on the main thread:
public class MainThreadBus extends Bus {
private final Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
mHandler.post(new Runnable() {
#Override
public void run() {
MainThreadBus.super.post(event);
}
});
}
}

Trigger HTTP request on button click

I am trying to trigger a HTTP post request on each button click to get some data from a webservice in android. I have created an async task to send the http request. My code is as follows :
Activity
public void sendHttpRequest(View view){
//gets called on button click
new MyHttpRequestTask(this).execute();
}
MyHttpRequestTask
public class MyHttpRequestTaskextends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void result) {
//Show received data
super.onPostExecute(result);
}
#Override
protected Void doInBackground(Void... arg0) {
//Send http request
return null;
}
}
I am having two problems, one is that the onPostExecute method is not being fired and the other is, if I press the button second time or multiple times, the task is not being executed. But somehow I think that onPostExecute method not being called is the reason for the task not being executed on second time. So, what am I doing wrong? How can I get rid of these issues? Thanks
Here is the full doInBackground method
#Override
protected Void doInBackground(Void... arg0) {
Looper.prepare();
GpsHelper gpsHelper = new GpsHelper();
LocationHelper locationHelper = new LocationHelper(mContext);
gpsHelper.turnGPSOn();
String location = locationHelper.getMyCurrentLocation();
...
String rawHtml = HttpHelper.sendPostRequest(postUrl, postParams);
HtmlHelper.processRawHtml(rawHtml);
Looper.loop();
return null;
}
It gets the current location using GPS, then the current address and posts them to a webservice, then the response from webservice is parsed and processed. Is the problem due to the GPS ?
Here you should call your method like
public MyHttpRequestTask myHttpRequestTask;
public void sendHttpRequest(View view){
//gets called on button click
//Status.PENDING -- got the status when your asynch task not run yet with same instance
//Status.RUNNING -- whether it running
if(myHttpRequestTask.getStatus() == Status.FINISHED || myHttpRequestTask.getStatus() == Status.PENDING){
myHttpRequestTask = new MyHttpRequestTask(this).execute();
}
}
if your asynch task is in running task then no need to start it again and it would not be start.
it would be feasible if you create a new object when your status of asynchtask is either finish or pending. and please put the log or system out in your post method so you can easily identify it whether it will be called. please check again.
Please do this in your code,
public void sendHttpRequest(View view){
//gets called on button click
new MyHttpRequestTask().execute();
}
public class MyHttpRequestTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//Send http request
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
//please put your code here.
}
}
try this code, and if any issues then please mention here.
After couple of days of head banging and debugging, I finally figured it out.
The onPostExecute was not being executed because of the GPS codes. Apparently, starting gps started few threads which were not terminated and hence onPostExecute was not being executed.

How to connect to a pending request with RoboSpice (Splash > Main activity)?

I have fews questions about RoboSpice (advanced usages).
Note: I use RoboSpice with OrmLite.
My Android application is composed of two main activities: the first one is SplashActivity (start on launch) and the second is MainActivity (lauched 5s after the Splash Screen). I perform 2 requests on splash:
SplashActivity
public class SplashActivity extends BaseActivity {
private fooRequest fooRequest;
private barRequest barRequest;
private exampleRequest exampleRequest;
private NewsRequest newsRequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
fooRequest = new fooRequest();
barRequest = new barRequest();
...
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Launch MainActivity...
}
}, 5000);
}
#Override
protected void onStart() {
super.onStart();
getSpiceManager().execute(fooRequest, new Integer(0), DurationInMillis.ONE_DAY, new fooRequestListener());
getSpiceManager().execute(barRequest, new Integer(1), DurationInMillis.ONE_DAY, new barRequestListener());
}
public final class fooRequestListener implements RequestListener<Foo> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Foo request failure");
}
#Override
public void onRequestSuccess(final Foo result) {
}
}
public final class barRequestListener implements RequestListener<Bar> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Bar request failure");
}
#Override
public void onRequestSuccess(final Bar result) {
}
}
}
The problem
My application logic is not really reliable: we can not be sure that requests are finished when MainAcitivty is launched. On the MainActivity I query my database with OrmLite for fetch some data and display them. So if request started on SplashActivity are not finished, my View display nothing.
Questions
1) I think that I need to add a listener to my pending requests (if such a request exists). On the RoboSpice Wiki, it said to use spiceManager.addListenerToPendingRequest. I have not managed to put it out, despite my tests. Maybe I'm doing something wrong? Can you give me code example? Resolved (see below)
2) Currently, the user is still waiting 5 seconds (timer Splash) before arriving at the home screen. How to check if data are in cache? With spiceManager.getDataFromCache() (it takes into account the expiration duration?).
3) What is the best retry policy to failed requests (a. at the first launched if database is not again created; b. if the database exists but data are expired)?
Edit
Question #1 resolved (I make a mistake in my original code) - #2 and #3 still relevant. Here's what to do (if it can help someone ...):
public class MainActivity extends BaseActivity {
private SpiceManager spiceManager = new SpiceManager(CalendarSpiceService.class);
...
#Override
protected void onStart() {
super.onStart();
spiceManager.start(this);
spiceManager.addListenerIfPending(Foo.class, new Integer(0), new fooRequestListener());
spiceManager.addListenerIfPending(Bar.class, new Integer(2), new newsRequestListener());
spiceManager.getFromCache(Foo.class, new Integer(0), DurationInMillis.ONE_DAY, new fooRequestListener());
spiceManager.getFromCache(Bar.class, new Integer(2), DurationInMillis.ONE_DAY, new newsRequestListener());
}
...
public final class fooRequestListener implements RequestListener<Foo> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Foo request failure");
}
#Override
public void onRequestSuccess(final Foo result) {
String test = result.getResult().iterator().next().getTitle();
Log.w("MyApp", "RoboSpice - Foo request OK ! "+test);
}
}
public final class barRequestListener implements RequestListener<Bar> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Bar request failure");
}
#Override
public void onRequestSuccess(final Bar result) {
Log.w("MyApp", "RoboSpice - Bar request OK ! "+test);
}
}
}
But I don't understand the aim of spiceManager.getFromCache here...
Thanks!
I would make the splash screen last longer. It looks like there is a realy ambiguity on its purpose : displaying a logo or something, or execute requests and display a "please wait message".
In the first case, it would be fine not to execute any request. In the second, it would be fine to wait for them to return.
I really prefer applications that, if they need a splashscreen at all, boot quickly. The splash screen should last less than 1 second. The first screen after it should handle the data querying and please wait message.
But to answer your questions :
2) yes, SpiceManager cache methods use cache expiry in the same way as executeRequest. You can check if there is something in the cache, but you will have to specify what you mean by "valid" data by specifying the expiry limit of the data.
3) I don't see the link with a retry policy and your overall problem. By default, RoboSpice has a retry policy to retry 3 times a failed request. If you can't get the data from your cache then it means there is nothing in it. If your listener's failure hook is invoked, then the network request failed.
Both could be the same listener, and your app should have a general mechanism to 1) display that something is wrong, 2) relaunch request if needed after a while (that could be a refresh button/item menu).
I hope I helped, but not so sure.

Has anyone implemented (or got more info) on Android SyncObserver

I am developing an app in Android that performs a background sync with a server (using SyncAdapter and authentication etc).
When the foreground app (with UI) is started, there maybe a background sync in progress, or optionally it may start one via a UI button.
I would like a way to "plug into" an on-going background sync (whether started by the system, or the periodic sync setting or the UI) and show it's progress in the foreground activity.
The ContentResolver documentation (http://developer.android.com/reference/android/content/ContentResolver.html) mentions a mysterious "SyncObserver" that has no link to javadoc and is not documented (that I can find).
There are some other pages around that mention it (http://www.chinaup.org/docs/migrating/m5-0.9/changes/android.content.ContentResolver.html) but I can't find out more about it.
Has anyone implemented this beast?
If not, does anyone have example code or recommendations on tracking the progress of a background sync in a foreground Activity?
Thanks for the answer.
Due to the async nature of the background sync your App (activity) could be started with a background sync already underway, which you detect with the status stored in a preference.
What I have done is to implement a SyncObserver class that implements the SyncStatusObserver interface and create/destroy on suspend/resume.
syncObserverHandle = ContentResolver.addStatusChangeListener( ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, new SyncObserver() );
This gets informed of any event related to sync (pending, started, etc) and I also check for status using
ContentResolver.isSyncActive();
The Android APIs for this are pretty basic, and you have to respect rules about what is done on the UI thread and what is not, but if anyone want to see my implementation just post a question and point me to it and I will answer with pleasure.
I had this same problem and ended up implementing it with a combination of 1) a broadcast from the SyncAdapter, and 2) using SharedPreferences to indicate status.
In the SyncAdapter, something like this this:
public static final String START_SYNC = "com.whatever.sync.start";
public static final String STOP_SYNC = "com.whatever.sync.stop";
public static final String SYNC_PROGRESS = "syncProgress";
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
// Add an integer to the shared settings to indicate the status
SharedPreferences settings = mContext.getSharedPreferences(Constants.PREFS, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putInt(SyncAdapter.SYNC_PROGRESS, 0);
editor.commit();
Intent intent = new Intent();
intent.setAction(START_SYNC);
mContext.sendBroadcast(intent);
//... do some stuff, setting SYNC_PROGRESS to other values and
// sending more broadcasts as the state changes
// When we are done, remove the "in progress" setting and store some
// other data
editor.putString(SyncAdapter.LAST_UPDATED, new Date().toString());
editor.remove(SyncAdapter.SYNC_PROGRESS);
editor.commit();
Intent stopIntent = new Intent();
stopIntent.setAction(STOP_SYNC);
mContext.sendBroadcast(stopIntent);
}
In the activity we do two things at resume 1) check the shared preference for whether a sync is currently in progress, 2) register to listen for broadcasts with a receiver.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// .. do some UI stuff
mReceiver = new SyncReceiver(this);
}
#Override
public void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SyncAdapter.START_SYNC);
intentFilter.addAction(SyncAdapter.STOP_SYNC);
registerReceiver(mReceiver, intentFilter);
showProgress();
}
public void showProgress() {
SharedPreferences settings = getSharedPreferences(Constants.PREFS, 0);
if (settings.contains(SyncAdapter.SYNC_PROGRESS)) {
// ... set the UI to show that a sync is in progress
} else {
// ... set the UI to show that a sync is NOT in progress
}
}
private class SyncReceiver extends BroadcastReceiver {
private MyActivity mActivity;
public SyncReceiver(MyActivity activity) {
mActivity = activity;
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(SyncAdapter.START_SYNC)) {
Log.i("#string/app_name", "Started sync");
mActivity.showProgress();
}
else if (intent.getAction().equals(SyncAdapter.STOP_SYNC)) {
Log.i("#string/app_name", "Started sync");
mActivity.showProgress();
}
}
}
This seems to work for me. I must admit I have a feeling that there are some potential issues with this due to the asynchronous nature of the broadcasts. Any input on improving my approach would be appreciated!

Categories

Resources