Sunday, 26 February 2012

Android: ProgressDialog does not show up

Android ProgressDialog can be used like iOS ActivityIndicator to show the app is blocked on some time-consuming work.

You cannot just show a ProgressDialog and then busy waiting a flag. If so the dialog will not show up. You need to busy wait in another thread.

Example:


final ProgressDialog dialog = ProgressDialog.show(
          MyActivity.this, "",
          "Loading. Please wait...", true);
new Thread(new Runnable() {
  public void run() {
    while (!isWorkDone)
      Thread.yield();
    dialog.dismiss();
  }
}).start();

Android: optimize app loading time

No one likes to wait for an app to launch for more than several seconds. It is critical to optimize the app loading time.

My app loads a large 3D model at launching. It is a custom binary format consisting of vertex array and index array. The vertex format is interleaved since it is from an iOS app. Each vertex is 8 floats: 3 for position, 3 for normal, and 2 for texture coordinates.

It is not suitable for Android if you use Java instead of NDK. So I write a small Java program to convert the binary file so that there is a position array, a normal array and a texture coordinates array. When I load the mesh, I can read all positions to a byte array, wrap it as a byte buffer, use it as a float buffer, then copy it to a direct float buffer, which could be used by OpenGL ES. Same for normals and tex coords. This optimization speeds up the file I/O by 50%. Although it is still kind of slow (4 secs). Most of time is spent copying from the byte array wrapped as float buffer to the direct float buffer.

Since 4 seconds is still slow for launching an app, I ran the mesh loading code in another thread, and add a flag for mesh ready. If mesh is not ready, the render code will skip rendering the mesh. With this change, the app is very fast to load.


Android: run time-consuming work in another thread

Many times you want to run time-consuming work in another thread instead of main thread because UI will stop response if you run it in main thread, and the user will frustrate.

It is easy to implement:

Just wrap up the time-consuming code with


new Thread(new Runnable() {
  public void run() {

  //... time-consuming work

  }
}).start();


Friday, 24 February 2012

Android: Using Flurry to gather information

Flurry is a mobile app analytics service for many platforms including iOS and Android.

It is very easy to use. Just register an account, then create a new app. It will generate an app id for your new app, then you can download its SDK.

For Android, you just need to add an external Jar to your Eclipse project, then add severa lines of code to init and end Flurry sessions.  You can then log events in your app.

Some usages:


  1. log screen size and dpi, so that you will know distribution of screen sizes, and prioritize your layout effort
  2. log time spent in time-consuming procedures, to prioritize optimization effort
  3. log frame rate for OpenGL ES, so that you may exclude some slow device from your app
  4. log supported OpenGL ES version. If OpenGL ES 2.0 is supported by 90% devices, you may start working on it
  5. log pages viewed, to rank the pages

Java: inline initializer for Map

Flurry has a function

FlurryAgent.logEvent(String event, Map<String,String> parameters)

It can be called with an inline initializer like this

FlurryAgent.logEvent("event", new HashMap<String,String>() {{
  put("key1","val1");
  put("key2","val2");
}});


Timing in Java

Timing in Java is easy.

long start = System.nanoTime();

long time = System.nanoTime()-start;

The unit is nanosecond. The long type is enough for 300 years.

Wednesday, 22 February 2012

Eclipse CDT: index not working

Eclipse CDT indexing of source codes is very important for large projects. Without it you cannot highlight a class or function and right click "open declaration". Manually go through the source tree to find a file is a nightmare.

The index of my C++ project was suddenly broken. I cannot highlight a class member and right click "open declaration". Also when I edit a header file, the outline panel does not show the class members.

I find this link
http://wiki.eclipse.org/CDT/User/FAQ#Why_does_Open_Declaration_.28F3.29_not_work.3F_.28also_applies_to_other_functions_using_the_indexer.29
but it does not work for me.

Then it occurs to me Eclipse no longer treated my project as a C++ project. Finally I got a solution:

Choose menu File/New/Convert to a C/C++ project. It took a while to index the source code. After that, everything works like a charm.


Sunday, 19 February 2012

Android: taking screenshots

In eclipse, choose DDMS perspective, then click the button for screenshot.

Android: null pointer exception after changing view id in xml

Sometimes after you modify view id in layout xml files, you get null pointer exception in seemingly irrelevant code due to findViewById returns null when finding irrelevant views.

This is due to modifying view id messed up the view id definitions of all views. A simple solution is to clean up your project and rebuild it (usually it should be auto rebuilt after cleaning up).

Wednesday, 15 February 2012

OpenGL: objects with color are displayed as black or white

When displaying with OpenGL, if the normals are not normalized, the objects may become black if the normals are too small and white if the normals are too big.

two constants affects normalization

GL_NORMALIZE: normalize normals. it is slower

GL_RESCALE_NORMAL: only scale normals based on scaling factor in modelview matrix. faster than GL_NORMALIZE. If the normals are not normalized from beginning, this won't fix them.

Android: artifacts in OpenGL ES display when dithering disabled

Android shows artifacts in OpenGL ES display when dithering is disabled. This is because by default the OpenGL view uses 16 bit color for performance reasons. Not all Android device supports 32 bit color. For better display dithering should not be disabled.

Tuesday, 14 February 2012

Android: exception when downloading web page without network connection


URL url = new URL("http://blah");
InputStream is = null;
is = url.openStream();

When there is no network connection, is is null. Using it will cause null pointer exception.

Check to make sure is is not null before using it.

Monday, 13 February 2012

Android: App name and icon not updated

Sometimes after you modify app name and icon through AndroidManifest.xml and rebuild it and install it, but the app name and icon on your device do not change. You may need to uninstall it and install again to see the change.

Android: How to delete development apps

Development apps installed through eclipse cannot be deleted by Android Market.

At home screen, press menu button, select Uninstall, then click the apps you want to install. Press back button once it is done.

Android: OpenGL ES: segfault displaying modified vertex array

I fixed a bug causing segfault when displaying a modified vertex array with OpenGL ES in Android.

Problem: I have an activity which contains an OpenGL view and a list view on top of it. The OpenGL view displays a vertex array by glDrawElements. Each time the user tap a list item in the list view, the onClick event handler will update the vertex array used by the OpenGL view. Segmentation fault will happen if I click the list items enough number of times (usually 4-10 times).

The vertex array is a FloatBuffer. When I modify it I call position() to move the position to the end of the buffer, then call put(value) in a loop to add new vertices. After that, I call position(0) to move the position to the beginning of the buffer.

I found that instead of using put(value), if I use put(position, value) to add new vertices, I won't get segfault. Then I noticed some weird triangles were displayed shortly when I click the list view.

Then I realized the event handler of the list view uses different threads than the thread displaying the vertex array in the OpenGL view. Therefore if I change the position attribute of the FloatBuffer, it could be used by the drawing thread and cause segfault.

Lessons learned:
1. make sure vertex array is not modified by other threads when displaying it
2. when debugging this kind of problem, try to removing problematic codes part by part until narrow down to one line of code