SyncAdapter process killed when app process is terminated - android

Why the SyncAdapter process (:sync) is killed when the app is swiped from the app switcher list ? i thought the whole intention here is to keep them decoupled.
EDIT:
Following is the code used. mUploadTask is a AsyncTask im executing that reads information from a sqlite table (using getContext().getContentResolver()) and uploads relevant data to a backend (using HttpPost). Very straight forward.
Also, i implemented only one onSyncCanceled() since my SyncAdapter doesnt support syncing of multiple accounts in parallel.
public class SyncAdapter extends AbstractThreadedSyncAdapter implements UploadTaskListener {
private static final String TAG = SyncAdapter.class.getSimpleName();
private static volatile UploadTask mUploadTask;
/**
* Set up the sync adapter
*/
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
/**
* Set up the sync adapter. This form of the
* constructor maintains compatibility with Android 3.0
* and later platform versions
*/
public SyncAdapter(
Context context,
boolean autoInitialize,
boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
}
#Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
MHLog.logI(TAG, "onPerformSync");
ContentResolver.setSyncAutomatically(account, authority, true);
if (mUploadTask == null) {
synchronized (SyncAdapter.class) {
if (mUploadTask == null) {
mUploadTask = new UploadTask(getContext(), this).executeOnSettingsExecutor();
MHLog.logI(TAG, "onPerformSync - running");
}
}
}
}
#Override
public void onSyncCanceled() {
MHLog.logI(TAG, "onSyncCanceled");
if(mUploadTask != null){
mUploadTask.cancel(true);
mUploadTask = null;
}
}

From the documentation:
Syncs can be cancelled at any time by the framework. For example a sync that was not user-initiated and lasts longer than 30 minutes will be considered timed-out and cancelled. Similarly the framework will attempt to determine whether or not an adapter is making progress by monitoring its network activity over the course of a minute. If the network traffic over this window is close enough to zero the sync will be cancelled. You can also request the sync be cancelled via cancelSync(Account, String) or cancelSync(SyncRequest).
A sync is cancelled by issuing a interrupt() on the syncing thread. Either your code in onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult) must check interrupted(), or you you must override one of onSyncCanceled(Thread)/onSyncCanceled() (depending on whether or not your adapter supports syncing of multiple accounts in parallel). If your adapter does not respect the cancel issued by the framework you run the risk of your app's entire process being killed.
Are you making sure your honoring the rules of the SyncAdapter framework?
Additionally, it would be nice to see some of your code to drill down to why the framework is cancelling your Sync...

The onPerformSync() works on a separate thread. So, you don't need to create any executor variables to implement the background work.
I had the same problem - my adapter has been using executor in onPerformSync() method, that perform operations (now - the one more thread).
That's a reason - in case the system doesn't see any job in onPerformSync() method in it's thread (because you've created executor that perform actions in another thread) - the onSyncCanceled() method will be invoked - it is just a question of time.
The short time operations will be done, but the long time (10 min) will be terminated by onSyncCanceled().
You can override onSyncCanceled() in your adapter - but you should understand the real problem and avoid it.
Here is the project sample https://github.com/Udinic/SyncAdapter. Do the client-server implementation in onPerformSync() method and have no problem.

Related

Writing Firebase database after app is swiped from recent list

I need to write three items of data in Firebase Realtime Database in case the user kill the app
from recent list while it's still running; I implemented a service in order to
update the database when onTaskRemoved is called.
In the manifest the service is declared with the option android:stopWithTask="false"
Here is the service
public class ServiceAppMonitoring extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
FirebaseDatabase mDatabase = FirebaseDatabase.getInstance();
SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
//Get some datas from Shared Preferences...
String path1 = "first/node/path";
mDatabase.getReference(path1).setValue(false);
if (condition) {
// Compose array of datas
List<Object> data2 = Arrays.asList(new Object[]{ ... });
String path2 = "second/node/path";
mDatabase.getReference(path2).setValue(data2);
// Compose array of datas
List<Object> data3 = Arrays.asList(new Object[]{ ... });
String path3 = "third/node/path";
mDatabase.getReference(path3).setValue(data3);
stopSelf();
} else {
stopSelf();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) { return null; }
}
Strangely only the first instruction is successful, the other two seem to be ignored... at least no data is written into database.
Further I've noticed another "unusual" behaviour, if I arrange all the DatabaseReferences in the
following way:
mDatabase.getReference("first").child("node").child("path").setValue(false);
no one of the instructions end up writing into database, to get (at least) the first one working I've to arrange this way:
mDatabase.getReference("first/node/path").setValue(false);
Can anybody kindly help me to understand why this happens?
Thanks
This is almost certainly because Firebase operations are asynchronous, and return immediately before the writes are complete. onTaskRemved is going to return before either of the database writes fully finish.
I'm guessing that your app process is going to die very soon, if not immediately, after onTaskRemved returns. This means that your database writes might not finish. Android doesn't know that these writes are pending, and it's not going to wait for them.
Since you don't have way from your service to tell Android to wait for these writes, you will have to schedule them for later. I suggest looking into using WorkManager to schedule the writes to happen in the background, whenever Android allows it. It might not be immediate, but WorkManager will make sure that any scheduled tasks will eventually complete.
Hey based on your answer I updated a small thing on database from onTaskRemoved like this
I already initialized the DatabaseReference in the onCreate method
DatabaseReference temp = FirebaseDatabase.getInstance.getReference().child("temp");
and in onTaskRemoved
temp.setValue(true);
this is getting executed and I added a onchange listener in onCreate method to listen for this values change and it worked like a charm. If you don't understand anything feel free to ask and let me know how you got over this problem:)
EDIT:
This is not working in all devices...
EDIT AGAIN: I simply used on Destroy method in my service and killed the service after completing the task and is working for now...
EDIT AGAIN AND AGAIN: OnDestroy is not working in android 6 or below I guess. I tested in Android 6 and it didnt work.

Android LiveData: How to avoid data transfer from the database when an app is re-opened from the background?

My Android app is listening on a Firebase Database. Whenever my activity becomes inactive, I stop the listener, and when the activity becomes active again, I restart listening. This is done using LiveData and the 'onActive' and 'onInactive' methods as below:
#Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
Using the debugger, I have noticed that when I press the back button and close the app, the onInactive method gets called, and the app goes in the background. When I reopen the app, by picking it among the apps that are in the background, the onActive method gets called. However, in this case, all my data is reread from the database which will consume data bandwidth.
My question is:
What is the best way to avoid the re-reading of the data every time the app is coming back from the background?
Thanks
What you will need to do is set a "timeout" of sorts on your LiveData so that it defers becoming inactive for as much delay as you deem appropriate.
I implemented a "LingeringLiveData" superclass for exactly this situation. You can see it in a project of mine on GitHub. It's written in Kotlin, but you should be able to port it to Java without much trouble.
Subclasses need to provide implementations for startLingering and stopLingering that mirror what you would normally do in onActive and onInactive.
Basically, it sets up a timer to delay a call to endLingering after onInactive has been invoked, but only if onActive isn't invoked before that time expires. This lets you app stop and start without losing the listener.
abstract class LingeringLiveData<T> : LiveData<T>() {
companion object {
private const val STOP_LISTENING_DELAY = 2000L
}
// To be fully unit-testable, this code should use an abstraction for
// future work scheduling rather than Handler itself.
private val handler = Handler()
private var stopLingeringPending = false
private val stopLingeringRunnable = StopLingeringRunnable()
/**
* Called during onActive, but only if it was not previously in a
* "lingering" state.
*/
abstract fun beginLingering()
/**
* Called two seconds after onInactive, but only if onActive is not
* called during that time.
*/
abstract fun endLingering()
#CallSuper
override fun onActive() {
if (stopLingeringPending) {
handler.removeCallbacks(stopLingeringRunnable)
}
else {
beginLingering()
}
stopLingeringPending = false
}
#CallSuper
override fun onInactive() {
handler.postDelayed(stopLingeringRunnable, STOP_LISTENING_DELAY)
stopLingeringPending = true
}
private inner class StopLingeringRunnable : Runnable {
override fun run() {
if (stopLingeringPending) {
stopLingeringPending = false
endLingering()
}
}
}
}
Jetpack LiveData KTX now also offers a liveData convenience constructor that accepts a similar timeout parameter and runs code in a coroutine. You won't be able to use at all from Java, but it's nice to know about.
The easiest way to decrease data downloads in this case is to enable disk persistence in the Firebase client.
With disk persistence enabled, the Firebase client will write any data it gets from the server to a local disk cache, cleaning up older data if the cache gets too big.
When the client is restarted, the client will read the data from disk first, and then only request updates from the server using a so-called delta sync. While this delta sync still transfers data, it should typically be significantly less than the total data at the location you listen on.

Android Thread Execution Slows Down when screen locks

Iam running this procedure to download some data and insert them into the database. The total procedure takes around 5 minutes. I noticed that while downloading, when the phone locks the screen and open it after 5 minutes, it will still downloading. It seems when locked download procedure slows down. Is there any explanation?
The execution time also slows down when pressing home button and becomes a background process, not only when sreen locks.
Thank you
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase sInstance;
#VisibleForTesting
public static final String DATABASE_NAME = "Database_db";
public abstract CustomerDao repoCustomer();
public static AppDatabase getInstance(Context context) {
if (sInstance == null) {
synchronized (AppDatabase.class) {
if (sInstance == null) {
sInstance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME).build();
}
}
}
return sInstance;
}
public void downloadCustomers(final String table){
executors.diskIO().execute(new Runnable() {
#Override
public void run() {
//download data and insert into database.
});
}
}
I believe it is something related with power management. Have you tried using a wake lock?
To test if that is your problem, simply add android:keepScreenOn="true" to the layout of the activity where the thread is started.
If it solves the problem and you donĀ“t need the screen on, consider reading this thread:
https://developer.android.com/training/scheduling/wakelock
To aquire a wakelock you must add this to your manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
And set manually the wake lock:
val wakeLock: PowerManager.WakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
acquire()
}
}
To manually release it, you can do it with:
wakelock.release()
Also, from the same source and it seems to me that this can be applied to your problem, check this out:
Before adding wakelock support to your app, consider whether your app's use cases support one of the following alternative solutions:
"If your app is performing long-running HTTP downloads, consider using DownloadManager.
If your app is synchronizing data from an external server, consider creating a sync adapter.
If your app relies on background services, consider using JobScheduler or Firebase Cloud Messaging to trigger these services at specific intervals."
Hope it helps.

Job manager in Android

I have a task to run several different jobs in Android app. Each job is long-running and cosumes network, database and file system much. Each job can be run manually by user or scheduled by AlarmManager. It is very important that each job runs till the end, so it needs to continue running after user leaves the app, or even when user does not open the app at all. Jobs have some ID attribute like this:
class Job {
int id;
}
I need this hypothetical JobManager to receive jobs and sort them by ID. If a job with id = 1 is already running, then JobManager should skip all the subsequent jobs with id = 1 until this job is finished. But if a job is submitted with id = 2, then it is accepted and can be run in parallel with the first job.
The jobs should also to keep wake lock until completed, like it is done in CommonsWare's WakefulIntentService.
I have several ideas how to implement this, but all have their drawbacks:
Subclass of the Service class that runs always in background and is automatically restarted, when killed for some reason. Drawbacks: it consumes resources even if not running anything, it is running on UI thread, so we have to manage some threads that can be killed by system as usual, each client has to start the Service and nobody knows, when to stop it.
WakefulIntentService from CommonsWare. Drawbacks: because it is IntentService, it runs only sequentially, so it cannot check for existing running job.
Boolean "running" flag in the database for each job. Check it every time we want to run a job. Drawbacks: too many requests to db, difficult to implement properly, sometimes 2 equal jobs still can run in parallel, not sure about flags staying "true" in case of any unexpected error.
Existing library disigned for this purpose. As for now except CWAC-Wakeful I have found:
Robospice: https://github.com/stephanenicolas/robospice
Android Job Queue: https://github.com/path/android-priority-jobqueue
but still I don't know, how to use these libraries to run exactly one centralized service, that whould accept jobs from any other Activity, Service, BroadcastReceiver, AlarmManager, etc, sort them by ID and run in parallel.
Please advise me what solution can be used in this case.
UPDATE: See below my own solution. I'm not sure, if it works in all possible cases. If you are aware of any problems that may arise with this, please comment.
This seems to be suited for the new JobScheduler API on Lollipop, then you will have to make a wrapper around it to implement all the features that the sdk implementation is missing.
There is a compat library if you need to implement this on versions below Lollipop.
If anybody faces the same problem, here is the solution I came up with. I used Robospice lib, because it is the most robust way of running some jobs on a Service and syncing results back to the Activity. As I did not find any ways to use this lib with WakeLocks, I extended 2 classes: SpiceManager and SpiceRequest. The new classes, WakefulSpiceManager and WakefulSpiceRequest, actually borrow CommonsWare's ideas about WakeLocks, the implementation is very similar.
WakefulSpiceManager:
public class WakefulSpiceManager extends SpiceManager {
private static final String NAME = "WakefulSpiceManager";
private static volatile PowerManager.WakeLock wakeLock;
private Context context;
public WakefulSpiceManager(Context context, Class<? extends SpiceService> spiceServiceClass) {
super(spiceServiceClass);
this.context = context;
start(context);
}
private static synchronized PowerManager.WakeLock getLock(Context context) {
if (wakeLock == null) {
PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME);
wakeLock.setReferenceCounted(true);
}
return wakeLock;
}
public <T> void execute(WakefulSpiceRequest<T> request, RequestListener<T> requestListener) {
PowerManager.WakeLock lock = getLock(context);
lock.acquire();
request.setLock(lock);
// explicitly avoid caching
super.execute(new CachedSpiceRequest<T>(request, null, ALWAYS_EXPIRED), requestListener);
}
}
WakefulSpiceRequest:
public abstract class WakefulSpiceRequest<R> extends SpiceRequest<R> {
private PowerManager.WakeLock lock;
public WakefulSpiceRequest(Class<R> clazz) {
super(clazz);
}
public void setLock(PowerManager.WakeLock lock) {
this.lock = lock;
}
#Override
public final R loadDataFromNetwork() throws Exception {
try {
return execute();
} finally {
if (lock.isHeld()) {
lock.release();
}
}
}
public abstract R execute() throws Exception;
}
So basically here we acquire the lock every time we are going to send a request from WakefulSpiceManager. After that the lock is passed to the WakefulSpiceRequest. When request finishes its work, it cleans the lock with release() method - this will happen even if the activity with WakefulSpiceManager is already destroyed.
Now we use those classes in usual Robospice's manner, with the only exception that we need to pass only WakefulSpiceRequests to execute on WakefulSpiceManager:
WakefulSpiceManager manager = new WakefulSpiceManager(context, MyService.class);
manager.execute(new WakefulSpiceRequest<MyResult>(MyResult.class) {
#Override
public MyResult execute() throws Exception {
return ...
}
}, new RequestListener<MyResult>() {
#Override
public void onRequestFailure(SpiceException e) {
...
}
#Override
public void onRequestSuccess(MyResult result) {
...
}
});
The new Workmanager will help you schedule tasks in any order you want. You can easily set constraints to the job that you want to be en-queued along with many other advantages over JobScheduler API or alarm manager. Have a look at this video for a brief intro - https://www.youtube.com/watch?v=pErTyQpA390 (WorkManager at 21:44).
EDIT: Updated my ans to show the capabilities of the new API
You will not need ids to handle the jobs with this one. You can simply enqueue the task and the rest will be handled by the API itself.
Some work case scenarios are
WorkManager.getInstance()
.beginWith(workA)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB)
.then(workC)
.enqueue();
WorkManager.getInstance()
// First, run all the A tasks (in parallel):
.beginWith(workA1, workA2, workA3)
// ...when all A tasks are finished, run the single B task:
.then(workB)
// ...then run the C tasks (in any order):
.then(workC1, workC2)
.enqueue();

n-1 async callback implementation

I'm re writing an IOS app in android. It's a task I've set to try and learn android.
In android I've being learning about different async messaging options. What I have found so far are:
Callbacks
Broadcasts
Message Handlers
I'm trying to decide which approach is best to suit my purposes. In my IOS application I have 10 screen objects and 1 coordinator object. This is my n-1.
The way my ios currently works is my screen object calls a work method in my coordinator passing itself as a delegate. The coordinator performs some async work and calls various methods on the delegate when the job is done : completed / failed with reason etc.
Multiple screen objects can request work to be done at the same time.
So far my feeling that the callback / message handler approaches in android is more like a 1-1 relationship.
I'm leaning towards using the local broadcast manager. This does seem more like NSNotification than delegate methods but seems made for n-1 relationships.
Is the broadcast manager the best way to achieve n - 1 async work ?
Also are my assumption about the 1-1 relationship of callbacks and handlers correct?
You can indeed use the broadcasts like NSNotification, but I would generally use broadcasts to message between different parts of my App, for example to communicate between a Service and an Activity, rather than within a specific part.
I don't see why you can't do exactly what you do in iOS on android as well. You would have a protocol in iOS defining what functions to call, and you can do the same in Java/Android by using interfaces.
In iOS you would have something like:
doStuffWithObject:(NSObject<SpecialStuff> *)object {}
while in Java you would have:
doStuffWithObject(SpecialStuff object) {}
with SpecialStuff being your protocol or interface.
Because you don't have performSelectorOnBackground in android its a bit more work. Either use Timers, perhaps a separate Thread in combination with Handlers or use ASyncTask depending on what serves you best and how big the asynchronous task is.
ASyncTask is definitely worth looking into.
You could of course also make use of Observer and Observable.
A simple example with an handler and a timer that notifies its listeners of a new time every second (note that the handler is created on the main thread in this case, such that you can send messages back similar to using performSelectorOnMainThread in iOS):
class SomeExample {
private final ArrayList<TimeListener> timeListeners;
private final Handler handler = new Handler();
private final TimeUpdateRunnable timeUpdateRunnable = new TimeUpdateRunnable();
public SomeExampleView {
timeListeners = new ArrayList<TimeListener>();
updateTimer = new Timer("General Update Timer");
TimeUpdateTask timeUpdateTask = new TimeUpdateTask();
updateTimer.scheduleAtFixedRate(timeUpdateTask, (60 * 1000) - (System.currentTimeMillis() % (60 * 1000)), 60 * 1000);
}
public void addTimeListener(TimeListener timeListener) {
timeListeners.add(timeListener);
}
public boolean removeTimeListener(TimeListener timeListener) {
return timeListeners.remove(timeListener);
}
class TimeUpdateTask extends TimerTask {
public void run() {
handler.post(timeUpdateRunnable);
}
}
private class TimeUpdateRunnable implements Runnable {
public void run() {
for (TimeListener timeListener : timeListeners) {
timeListener.onTimeUpdate(System.currentTimeMillis());
}
}
}
}
And my listener interface which is similar to Observer
public interface TimeListener {
void onTimeUpdate(long time);
}

Categories

Resources