All posts by Karol Depka Prądziński

About Karol Depka Prądziński

https://karoldepka.com/about

Nice article about Eclipse MAT

Memory Analysis for Android Applications | Android Developers Blog

Especially the “Comparing heap dumps with MAT” section has helped me.

Would be nice to have some pointers to dealing with native memory as well…

Advertisements

Catch.com closing down?

Today I received the following e-mail:

 

Dear Catch user,
Catch has made the difficult decision to take the company in a different direction. As such, we will be terminating service next month. We value our users and have greatly enjoyed providing Catch to you and millions of others over the last several years.

Catch will no longer be available after August 30, 2013. We apologize for the disruption this may cause to you. We’ve created an export tool to help you keep your notes and transition to another service. Please follow these directions to download your data before that date. Afterwards, you will no longer be able to access your notes on the web and our mobile apps, Catch Notes and AK Notepad, will no longer sync content across devices or allow collaboration. Click here for help and information about other note taking apps.

If you have any questions or concerns, please email us at support@catch.com. We apologize if we are not able to respond to all emails.

Thank you again for your support,
The Catch Team

What could be the reasons and what “new direction” could it be?

 

 

To avoid distractions, I don’t enter facebook.com… (but wait, there’s a twist)

When I want to do something particular on Facebook (e.g. message someone or post an update), I don’t go through facebook.com.

Instead, to avoid random updates distracting/tempting me, I enter only through following bookmarks :

Voilà, FB becomes less of a time vampire and more of a tool :-).

I’ve just saved You thousand of hours ;-).

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.

More responsible (atomic) way to write files in Java – AtomicFileWriter

Did You ever have a situation in which a certain application has corrupted its file (e.g. config) by leaving incomplete or empty file due to an error during writing of the file?

Here is my proposed solution to prevent this and thus make applications more robust.

package com.karoldepka.librelib.android.io;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/** Writes to a file in such a way to ensure that the previous contents
 * of the file will stay intact if the writing does not complete successfully.
 * 
 * Prevents a scenario in which the target file is left corrupted
 * with incomplete (or even empty) content due to failed attempt to write.
 * 
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public class AtomicFileWriter extends BufferedWriter {

	private final AtomicFileHandler partFileHandler;
	
	public AtomicFileWriter(File file, boolean append)
			throws IOException {
		super(new FileWriter(AtomicFileHandler.getPartFile(file), append));
		partFileHandler = new AtomicFileHandler(file);
		
	}
	
	@Override
	public void close() throws IOException {
		super.close();
		partFileHandler.onClose();
	}

}
package com.karoldepka.librelib.android.io;

import java.io.File;

/**
 * Handles partial/old file for atomic writing.
 * 
 * Logic in this class is separated from AtomicFileWriter to avoid tying it
 * to Writer (as opposed to other uses in e.g. OutputStream).
 * 
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public class AtomicFileHandler {
	private final File targetFile;
	
	public AtomicFileHandler(File targetFile) {
		this.targetFile = targetFile;
	}
	
	public static File getPartFile(File file) {
		return getFileWithSuffix(file, ".part");
	}

	public static File getFileWithSuffix(File file, String suffix) {
		return new File(file.getParentFile(), file.getName()+suffix);
	}
	
	public void onClose() {
		renamePartFileToTarget();
	}

	public void renamePartFileToTarget() {
		File partFile = getPartFile();
		File fileWithOldSuffix = getFileWithSuffix(targetFile, ".old");
		
		if ( targetFile.exists() ) {
			targetFile.renameTo(fileWithOldSuffix);
		}
		partFile.renameTo(targetFile);
		fileWithOldSuffix.delete();
	}

	public File getPartFile() {
		return getPartFile(targetFile);
	}

}

More strongly enforced canonical mappings using PutOnceMap

Ever used a Map for a canonical mapping? But what if someone due to a bug, creates another instance when a canonical instance is already “canonized”?

This class ensures this potential bug to be fail-fast.

package com.karoldepka.librelib;

import java.util.HashMap;

/**
 * A HashMap for canonical mapping which ensures fail-fast behavior if someone tries
 * to change the mapping of an already "canonized" key+value.
 * 
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public class PutOnceMap<TKey, TValue> extends HashMap<TKey, TValue> {
	
	@Override
	public TValue put(TKey key, TValue value) {
		TValue currentVal = get(key);
		if ( currentVal != null ) {
			if ( currentVal != value ) {
				throw new RuntimeException("Cannot change mapping for key " +key + " to value " +
						value + " from value " + get(key));
			} else {
				// unnecessary put
				return currentVal;
			}
		} else {
			return super.put(key,value);
		}
		
	}
	
}