Saturday, April 24, 2010

Avoiding OutOfMemoryError

Some Java ME applications load all their resources from its own jar file, but other apps load resources dinamically, for example,
from a network connection.

My Books application can open text files using an
Open File Dialog I have created.
But what happens if a file is too big to open?

If the Java Virtual Machine raises an Exception current version (1.3)
will show an error message requesting the user to send me an email with the details: exception type and message (if available).
Because of this I have already received many emails with IOException details.

But I was not treating Java Errors, only Exceptions. Because of this some users might see "Unhandled Error" messages shown by Java
Virtual Machine and will not know what to do. Next version will fix this and show a better message.
That is a good fix but does not solve the problem.

One thing I have learned is that reacting to Exceptions and Errors is not a good approach. The best thing to do is to antecipate
such situations and a situation we can antecipate is the OutOfMemoryError.

First, we need to know how much free memory is available. This can be done with
Runtime class.
Before trying to open a file I check if its size fits the current available memory with the following method:


boolean isThereEnoughMemoryToOpen (FileConnection file)
throws IOException
{
return Runtime.getRuntime().freeMemory() < file.fileSize();
}


If the method returns false (or throw an Exception) I do not open the file and show an error message to the user.

Related topics:

Saturday, April 17, 2010

Using pointerDragged method

Some cell phones do not have a keyboard. They only have a touch screen.
In this case, if you have a Canvas
screen on your application you will have to rely only on pointer methods.

This fact came to me because a user of my Books
application have such a phone and sent me an email informing that he could not go back to a previous page.
I checked the application source code and found out the reason. The code used pointer methods only to go forward, never backwards.
I have developed (but not released) a solution using pointerDragged and here are the details.

For functional backward compatibility I had to differ from two user actions:


  • touch and release - methods call order is: first pointerPressed and then pointerReleased.
  • touch, dragg and release - methods call order is: first pointerPressed, then a lot of pointerDragged calls and then pointerReleased.

A new boolean attribute was added: pointerDragging. This attribute receives true at pointerDragged and is checked on pointerReleased.
If it is false I go forward to next book page. Below is how I use this attribute:

protected void pointerDragged(int x, int y) {
pointerDragging = true;
// ... more code. I will show this in a while
}
protected void pointerReleased(int x, int y) {
if (pointerDragging == false) {
// move the text down. Torwards the end of the file
this.keyPressed(Canvas.KEY_NUM8);
}
pointerDragging = false;
}


Now I had to add the new user action: text dragging. To help know where the dragging is moving a new class was created: Point.

class Point {

int x, y;

void set (int x, int y) {
this.x = x;
this.y = y;
}

boolean rightOf (int x) { return x < this.x; }

boolean leftOf (int x) { return x > this.x; }

boolean above (int y) { return y > this.y; }

boolean below (int y) { return y < this.y; }
}

A new Point attribute was added: lastPoint. It is initiated on constructor and used on pointerPressed to store the first point the user touched the screen.

protected void pointerPressed(int x, int y) {
this.lastPoint.set(x, y);
}

As pointerDragged method is called a lot of times I could not move to previous/next page at each call to this method.
I had to check if current x,y point was far enough from the lastPoint stored on pointerPressed.
And "far enough" came to me as font.getHeight(), where font is the Font instance used to draw the text on paint method.

Below is my final source code for pointerDragged method:


protected void pointerDragged(int x, int y) {
pointerDragging = true;

if (this.lastPoint.rightOf(x + font.getHeight())
|| this.lastPoint.above(y - font.getHeight())) {
// move the text up. Torwards the beginning of the file
this.keyPressed(Canvas.KEY_NUM2);
this.lastPoint.set(x, y);
}
else if (this.lastPoint.leftOf(x - font.getHeight())
|| this.lastPoint.below(y + font.getHeight())){
// move the text down. Torwards the end of the file
this.keyPressed(Canvas.KEY_NUM8);
this.lastPoint.set(x, y);
}
}

I check both x and y values because the user will be able to move between pages using up/down or left/right touch movements.
Next release of Books is planned for this week. Stay tuned!

Related topics: