Wednesday, August 17, 2011

Leaked IntentReceiver?

Ok, so total noob here. Learned the hard way. It's tempting to register an anonymous inner class instance as a broadcast receiver and forget about it, like this:

 registerReceiver(new BroadcastReceiver() {
   @Override
   public void onReceive(Context arg0, Intent arg1) {
    // code here...
   }
  }, new IntentFilter(...));


If you do that, when you switch orientation (ctrl-F11 in the emulator), you'll see an exception like this:

ERROR/ActivityThread(1585): Activity com.foo.SomeActivity has leaked IntentReceiver com.foo.SomeActivity$2@405266b8 that was originally registered here. Are you missing a call to unregisterReceiver()?


So using an anonymous inner class is a bad idea.  You have to hang onto the reference to the BroadcastReceiver instance in a member variable of the Activity and release it in onDestroy:

 @Override
 protected void onDestroy() {
  super.onDestroy();
  unregisterReceiver(syncCompleteReceiver);
 }

Wednesday, August 10, 2011

Filtering Lists

I suppose for filtering a ListView that has only a single string in each item is probably pretty easy (haven't actually tried it yet). My first try required me to filter (as the user types on the keyboard) a ListView that is displaying several TextViews for each item.  Like this:


I had a hard time finding documentation on how to do this, and it took me a little while to figure it out.  There might well be a better way than this, but this is how I managed it.

The application is using a Cursor and SimpleCursorAdapter to get the data in the ListView.  All that I had to do was set the FilterQueryProvider to an anonymous inner class, in which the overridden runQuery method re-queries the database.  Like this:

  adapter.setFilterQueryProvider(new FilterQueryProvider() {

   @Override
   public Cursor runQuery(CharSequence startingWith) {
    try {
     Cursor newCur = dao.findByLastName(
       startingWith);
     startManagingCursor(newCur);
     return newCur;
     
    } catch (Exception ex) {
     // do something reasonable here
    }
    return cur; // original cursor if something went wrong.
   }
  });



Tuesday, August 9, 2011

RelativeLayout

Android's RelativeLayout is the best thing since Swing's GridBagLayout.

The training I went through (and most of the online material I looked at) used mostly LinearLayout -- and, in fact, that's what the Android Eclipse plugin defaults to when you create a new layout XML file.

Far better to start out with RelativeLayout.  It looks like this:
<relativelayout 
android:layout_height="fill_parent" 
android:layout_width="fill_parent" 
android:orientation="vertical" 
xmlns:android="http://schemas.android.com/apk/res/android">
    

<textview android:id="@+id/textView1" 
android:layout_alignparentleft="true" 
android:layout_alignparenttop="true" 
android:layout_height="wrap_content" 
android:layout_marginleft="68dp" 
android:layout_margintop="75dp" 
android:layout_width="wrap_content" 
android:text="TextView">
</textview>
    <textview 
android:id="@+id/textView2" 
android:layout_alignleft="@+id/textView1" 
android:layout_below="@+id/textView1" 
android:layout_height="wrap_content" 
android:layout_margintop="42dp" 
android:layout_width="wrap_content" 
android:text="TextView"></textview>

</relativelayout>



You can do things like:
android:layout_below="@id/textView1"
  android:layout_toRightOf="@id/thatsId"

It makes life much easier. In the GUI editor, it looks like this...


Doing things Asynchronously on Android

Did I mention I'm still learning Android? Here are some things I wrestled with getting started.

I had a background job I needed to run in this application, and I wanted to display progress as it worked.  I saw several possible solutions:
  1. An Activity with a Java Thread
  2. An Activity with an Android AsyncTask
  3. A Service with a Java Thread
  4. An Intent Service
I didn't know about Intent Services when I started, and because I needed to update the UI based on feedback from the worker thread, I started with AsyncTask.  I really struggled with using AsyncTask because it lacks good separation of concerns.  Your background code is stuck in the same class as your UI update code.  Not very nice.

I also wanted to have my background code in a class that had no Android framework dependencies.  Just a pojo.  That made it even more difficult.

The problem was resolved when I realized that the work I needed to do in the background was best thought of as a service.  AsyncTask seems to be really just for small, but long-running foreground tasks.  You use AsyncTask to keep from blocking the  main thread in those cases.

An AsyncTask class looks like this:

AsyncTask task = new AsyncTask() {
			@Override
			protected Object doInBackground(Object... arg0) {
				return null;
			}
			
			@Override
			protected void onCancelled() {
				super.onCancelled();
			}
			
			@Override
			protected void onPreExecute() {
				super.onPreExecute();
			}
			
			@Override
			protected void onPostExecute(Object result) {
				super.onPostExecute(result);
			}
			
			@Override
			protected void onProgressUpdate(Object... values) {
				super.onProgressUpdate(values);
			}
		};

The "onWhatever"  methods get called by the framework on the main UI thread, while the doInBackground runs in a separate worker thread when you start the task.

I then went down the plain android.app.Service path.  Extending service works fine, but you have to manage the service life cycle, and things like threading and re-entrance, yourself.

android.app.IntentService relieves you of that burden.  All you have to do is:

public class FooService extends IntentService {

	public FooService() {
		super("FooService");
	}

	public FooService(String name) {
		super(name);
	}

	@Override
	protected void onHandleIntent(Intent intent) {

	}
}

onHandleIntent will execute in a background thread.  To start the service (from an activity):

startService(new Intent(this, FooService.class));

Of course, the irony is that the UI is not currently getting updates from the service! It will in a future release, though. And I'm still extending a framework class. I'll be taking another this whole thing again soon.

Dry Erase Crayons?

Okay, before I post some Android stuff, my kids introduced me this week to dry-erase crayons. They're made by Crayola and come in a pack of eight -- black, blue, purple, brown, red, green, yellow and orange.

They work great and it seems like they'll last forever. No more markers drying up on me.

The only drawback so far is that they obviously don't glide effortlessly across the surface of the board, like the markers do. But it's not worse than writing on paper.

I'm a big fan.

Going Mobile

So I'm making the switch to mobile application development -- initially with Android, since I don't know Objective-C yet. I'm going to post a series of things that I'm learning as I go.

A few rapid-fire posts up front, since I'm already behind in posting. But who cares anyway? Nobody reads this but me. :-)