Re-usable Auto-saver class for Android

It can also save CPU usage (and thus battery) by doing deferred updates to protect from “machine-gun” updates/saves on e.g. every keypress from the user.

Benefits:

  • can save CPU cycles, battery, storage medium wear, bandwidth
  • re-usable, encapsulated, separated from business logic
  • LGPL license

How I use it in my app (ttps://play.google.com/store/apps/details?id=com.dayagendaplanner&hl=en) :

  • Deferring/batching of saves to persistent store (SD card)
  • Deferring/batching updates to registered future alarm notifications
  • Deferring/batching of re-computations of times in the table. Although this is less sweet because AutoSaver works best if its use does not delay UI updates.

Here’s the code.

https://github.com/karol-depka/LibreLib/blob/master/LibreLibAndroid/src/com/karoldepka/librelib/android/AutoSaver.java :

package com.karoldepka.librelib.android;

import android.os.Handler;

/**
 * Re-usable class for batched/delayed saves/updates.
 * Save occurs: at most {@link #delayMs} milliseconds after modification is reported
 * (which is also the moment of timer restart).
 * After save, the timer stops.
 * After a modification is reported, timer restarts.
 * If the timer is already running, it will not start again (will run its course to the end),
 * otherwise there would be a risk of never saving if the modifications were reported
 * at certain big frequency.
 *
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public abstract class AutoSaver {
	public boolean saveTimerRunning = false;

	public class RunnableSave implements Runnable {
		@Override public void run() {
			saveNowIfNeeded();
			saveTimerRunning = false;
		}
	}

	private final int delayMs;
	private final Handler mHandler = new Handler();
	private Runnable runnableSave = new RunnableSave();

	public AutoSaver(int delayMs) {
		this.delayMs = delayMs;
	}

	protected abstract void saveCustom();

	public void documentModified() {
		ensureSaveTimerRunning();
	}

	private void ensureSaveTimerRunning() {
		if ( !saveTimerRunning ) {
			mHandler.postDelayed(runnableSave, delayMs);
			saveTimerRunning = true;
		}
	}

	public void saveNowIfNeeded() {
		// TODO: only save if modified
		forceSaveNow();
	}

	/** Useful e.g. when shutting down the app*/
	public void forceSaveNow() {
		saveCustom();
		stopSaveTimer();
	}

	private void stopSaveTimer() {
		mHandler.removeCallbacks(runnableSave);
		saveTimerRunning = false;
	}

	public void destroy() {
		saveNowIfNeeded();
	}
}

Example usage:

https://github.com/karol-depka/LibreLib/blob/master/LibreLibAndroidExamples/src/com/example/librelib/android/examples/AutoSaverExample.java :

package com.example.librelib.android.examples;

import com.karoldepka.librelib.android.AutoSaver;

public class AutoSaverExample {
package com.example.librelib.android.examples;

import com.karoldepka.librelib.android.AutoSaver;

public class AutoSaverExample {

	private final AutoSaver autoSaver = new AutoSaver(2000) {
		@Override public void saveCustom() {
			// save/push/update somewhere
		}
	};

	/** Called when the user modifies the data */
	public void onModified() {
		autoSaver.documentModified();
	}

	public void onForceSave() {
		autoSaver.forceSaveNow();
	}

	public void onDestroy() {
		autoSaver.destroy();
	}

}

Creative Commons License

This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s