Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Monday, April 25, 2011

Better findViewById that doesn't require you to type-cast the result

If you started developing Android apps, you must have known the findViewById method. That is the method that glues UI and code.

However, findViewById returns the generic View, so you need to type-cast it to the specific view if you want to do widget-specific operations on it (e.g. setText).

TextView lAddress = (TextView) findViewById(R.id.lAddress);

or,

ImageButton bSubmit = (ImageButton) findViewById(R.id.bSubmit);

That's so annoying, even more if you change the type of the widget with similar but unrelated ones, like swapping a Button with an ImageButton.

Solution: use a generic method that returns you an object of the type that you request.

Paste the 2 methods below anywhere you like.

/**
 * Convenience method of findViewById
 */
@SuppressWarnings("unchecked")
public static <T extends View> T getView(View parent, int id) {
 return (T) parent.findViewById(id);
}

/**
 * Convenience method of findViewById
 */
@SuppressWarnings("unchecked")
public static <T extends View> T getView(Activity activity, int id) {
 return (T) activity.findViewById(id);
}

Now, you can just call them like:

TextView lAddress = getView(this, R.id.lAddress);

// or, if not inside an activity, let's say in an Adapter's getView:
View res = convertView != null? convertView: getLayoutInflater().inflate(R.layout.item_person, null);
TextView lAddress = getView(res, R.id.lAddress);

Friday, April 15, 2011

Call getItem instead of parent.getItemAtPosition

On your list item click listener, usually you will need to get the item clicked. You can do it with:

News news = (News) parent.getItemAtPosition(position);


However, I don't recommend that, mainly because of the cast. If you change the adapter to operate on items of different type, the compiler will not catch the error.

Instead, access the adapter directly.


News news = adapter.getItem(position);


This is possible only if you make the adapter return the correct item type instead of Object:

public News getItem(int position) {
return list == null || position >= list.items.size()? null: list.items.get(position);
}


(for long-time Java user, you may not have noticed that that was possible starting Java 5.)

Tuesday, September 21, 2010

Solution to: onContextItemSelected not called after selecting a context menu

Here is a solution to Android development problem, where the onContextItemSelected method is not called even after you see the context menu popup and selecting one of the items there.

1. Make sure that registerForContextMenu has been called, with the parameter the view you have the context menu on. For ListActivity with a context menu on the items, call registerForContextMenu(getListView()).

2. If you have an options menu on your activity (the one that pops up from the bottom of the screen when you press the hardware/softshell MENU button, you may be interrupting the event flow when you override onMenuItemSelected method. Overriding the onMenuItemSelected was taught in the Notepad v2 tutorial source code. That was partially wrong. You should not override it, instead you should override onOptionsItemSelected, and return false when the menu item is not handled. Example:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.menuImpor) {
        // do your work...
        return true;
    } else if (item.getItemId() == R.id.menuEkspor) {

        // do your work...
        return true;
    }
    return false; // no need to call super.onOptionsItemSelected(item)
}

That way, your onContextItemSelected should now be called correctly.

In a nutshell, "Menu" is the generic term for both "Context" and "Options" menu. Make sure you pay attention to the naming of the methods.

Tuesday, April 6, 2010

String.format in Android is extremely slow!

I'm used to using String.format to construct messages. Even when I don't need to specify width or number of decimal places. It just looks neater to the eyes.

For example, on the Alkitab (Bible) application, I wrote this for debugging:

Log.d("alki", String.format("tebakKitab fase 3: dengan %s:%d skor %d", ref.pendek, ref.pos, skor));

Each of "my operation", involving about 70 calls to the above line, takes about 300 ms. I thought that was acceptable.

But when I tried to build a game engine, I found a bottleneck somewhere that drags the fps. I found it to be the String.format call, which takes about 6ms for just one call! That's ridiculously slow.

Guess what: when I change the above line to:

Log.d("alki", "tebakKitab fase 3: dengan " + ref.pendek + ":" + ref.pos + " skor " + skor);

"my operation" takes only 22 ms!

Okay, once again: Don't use String.format in Android applications!

Note: When I traced method calls to know what makes it slow, String.format apparently calls a very deep code, something with DecimalFormatter, even CurrencyFormatter even though I didn't use it. It also calls com.ibm.icu.** packages. It really has a huge logic inside.

Thursday, December 17, 2009

Faster reading UTF-8 encoded file in Android

I created an Android application which reads some text files from a raw resource. The text files are encoded in UTF8. Therefore, I straight away wrote the code to convert bytes from the file into characters.

InputStreamReader in = new InputStreamReader(new BufferedInputStream(resources.openRawResource(R.raw.textfile)))
int c = in.read(); // read a character, and so on.

But, reading a 10KB file takes almost a minute on the Android 1.5 emulator! I wondered what made that so slow, in my Nokia phone, the same program written in Java ME takes less than a second to do the same thing.

By using Traceview, I found out that most of the time is spent on the UTF-8 decoding from bytes to characters. Android's Java implementation uses IBM ICU for character encoding. And it seems to be overkill to just decode UTF-8. Hence, the solution is to create own implementation if UTF-8 decoder. (Some concept taken from Go source, less the error-checking overhead and only look for max 16-bit characters.)

public class Utf8Reader implements Closeable {
    private InputStream in_;
    public static final char replacementChar = 0xFFFD;

    public Utf8Reader(InputStream in) {
        in_ = in;
    }

    public int read() throws IOException {
        int c0 = in_.read();

        if (c0 == -1) {
            // EOF
            return -1;
        }

        if (c0 < 0x80) {
            // input 1 byte, output 7 bit
            return c0;
        }

        int c1 = in_.read();

        if (c1 == -1) {
            // partial EOF
            return -1;
        }

        if (c0 < 0xe0) {
            // input 2 byte, output 5+6 = 11 bit
            return ((c0 & 0x1f) << 6) | (c1 & 0x3f);
        }

        int c2 = in_.read();

        if (c2 == -1) {
            // partial EOF
            return -1;
        }

        // input 3 byte, output 4+6+6 = 16 bit
        return ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f);
    }

    @Override
    public void close() throws IOException {
        in_.close();
    }
}

(Please add the required import by yourself.) The result is satisfying: the 10KB file is now loaded in about 1 second in the emulator, and almost instantly on the device.

Thursday, August 6, 2009

Multiple Return Values in Java

How can we have multiple return values in Java? We cannot formally have it, since each method can only have no return value (void) or single return value (int, long, Object, String, etc.)

One of the solution to emulate it is to create a class that contains two fields, one for each return value. But that will make you tired because you need to create it every time you need it (not including the time used to think what the class name should be).

In my case, I solved this problem by using a Pointer class. It works similar to C in which you can have multiple return values by passing a pointer to a value that will be modified to the function arguments like: size = fread(pbuf, size, count, pfile);

The pointer class is as follows:

public class Pointer<T> {
  public T value;
  public static <T> Pointer<T> create() {
    return new Pointer<T>();
  }
}

Let's say you have a method that needs to return two values: result and error code. The method for our example will be:

byte[] downloadFile(String url, Pointer<Integer> errorCode) {
  ...(really download)...
  if (errorCode != null) {
    errorCode.value = 200; // example only
  }
}

To use the method, we first create the pointer to store the error code as follows:

Pointer<Integer> errorCode = Pointer.create();
byte[] file = downloadFile("http://biginteger.blogspot.com/", errorCode);
System.out.printf("File downloaded (%d bytes) with error code %d", file.length, errorCode.value);

The reason of the create() is to eliminate repeated typing of the type parameter:

Pointer<Integer> errorCode = new Pointer<Integer>();