[Android Q&A, Help & Troubleshooting] Hans Schmucker:
Here you go. I added a few comments:

package net.tapperware.simpleanimation;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;

public class SimpleAnimationActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//This will hold our single bitmap which will contain all frames stacked ontop of each other
Bitmap animBmp=null;
try {
//First, load the file: This is supposed to be in the assets folder of the project
InputStream animStream=this.getAssets().open("anim.bmp");
//BitmapFactory needs a BufferedInputStream, so let's just convert our InputStream
BufferedInputStream animBufStream = new BufferedInputStream(animStream);
//OK, now we decode it to an Android-Bitmap
animBmp = BitmapFactory.decodeStream(animBufStream);
//And finally, we clean up
animBufStream.close();
animStream.close();
} catch (IOException e) {
e.printStackTrace();
}

//This will be used to operate the loop... looping on Android needs a Runnable, and this class provides it. When
//run, it will invalidate the view, which causes onDraw to be called.
class AnimLoop implements Runnable{
private View view;
AnimLoop(View v){ view=v; }
@Override public void run() { view.invalidate(); }
}

//And here the actual view
class AnimView extends View{
//Time between frames
public int frameTime=100;
//Number of frames
public int frames=-1;
//Width of each frame
public int frameWidth=0;
//Height of each frame
public int frameHeight=0;
//The previous frame number shown
public int lastFrame=0;
//This is our Runnable that will call invalidate()
public AnimLoop loop=null;
//Of course, the actual Bitmap
public Bitmap animBmp=null;
//Just a default Paint object, since it's required by drawBitmap
public Paint paint=null;
public AnimView(Context ctx, Bitmap bmp,int frms) {
super(ctx);
//Assign the args to our attributes
animBmp=bmp;
frames=frms;
//Since the images are stacked ontop of each other, each frame is widthOfBitmap * (heightOfBitmap / numberOfFrames )
frameWidth=bmp.getWidth();
frameHeight=bmp.getHeight()/frames;
//And finally, our dummy height
paint=new Paint();
//And the Runnable that will cause invalidate to be called
loop=new AnimLoop(this);
}
protected void onDraw(Canvas c){
//Draw the rect that corresponds to the current frame

c.drawBitmap(
animBmp
//We need to move down by frame number*the frame index
,new Rect(0,lastFrame*frameHeight,frameWidth,(lastFrame+1)*frameHeight)
//This is our target rect... draw wherever you want
,new Rect(0,0,frameWidth,frameHeight)
//And our standard paint settings
,paint);

//Increase the frame number by one, if it's >= the total number of frames, make it wrap back to 0
lastFrame=(lastFrame+1)%frames;

//And ask for the next frame to be drawn in frameTime milliseconds.
this.getHandler().postDelayed(loop, frameTime);
}
};

//Arguments: Activity/Context , Bitmap, Frame Count
AnimView v=new AnimView(this,animBmp,3);
setContentView(v);

}
}