CategoryDevelopment

Tutorial: How to play animated GIFs in Android – Part 2

Animated GIFs in Android is a difficult topic. It is an issue that has been discussed heavily and still, it is very difficult for developers to bring GIFs to life. There are three ways to animate GIFs on Android, each of them has its pros and cons. Each part of this series will cover one of these approaches.

Getting started

For this example, we will use an image I found on gifs.net, it’s this one:

I will store it in our project’s asset folder and name it ‘piggy.gif’. We will also use an Activity to set the views we define as content views. If you want to know everything about playing GIFs, please start at part one.

Approach 2: Extracting the Bitmaps

For this approach, we will use the GifDecoder class published here on googlecode. It’s Apache licensed, so don’t worry. What this class esentially does is, it extracts the different bitmaps from the given stream so you can use it the way you want.

To get started, download this class first. Place it somewhere in your project, maybe in a util package.
Now, we create a new class which inherits from ImageView:

   public class GifDecoderView extends ImageView

We create a constructor with a Context and an InputStream, just like in the first part of this series. This time, we call a method playGif(InputStream) and pass it our InputStream:

   public GifDecoderView(Context context, InputStream stream) {
      super(context);
      playGif(stream);

We give our class five member variables: A boolean which will state whether the thread we will use to play our animation is runningor not, an instance of the GifDecoder-class you just downloaded, a Bitmap which stores the different frames of the animation, a Handler to post our updates to the UI-thread and a Runnable that will arrange that the Bitmap we just defined will be drawn using the Handler:

   private boolean mIsPlayingGif = false;

   private GifDecoder mGifDecoder;

   private Bitmap mTmpBitmap;

   final Handler mHandler = new Handler();

   final Runnable mUpdateResults = new Runnable() {
      public void run() {
         if (mTmpBitmap != null && !mTmpBitmap.isRecycled()) {
            GifDecoderView.this.setImageBitmap(mTmpBitmap);
         }
      }
   };

Let’s take a look at playGif(InputStream). First, we need to initialise mGifDecoder. After that, we let it read our stream and set mIsPlayingGif to true, so that our thread can run:

   private void playGif(InputStream stream) {
      mGifDecoder = new GifDecoder();
      mGifDecoder.read(stream);
      mIsPlayingGif = true;

Now we need to define our thread. We retreive the frame count of our GIF’s frames and the number of repetitions. When GifDecoder.getLoopCount() returns 0, this means the GIF should be played infinitely. Now for every frame, we receive the according Bitmap by calling getFrame() on the GifDecoder. We post our new Bitmap using the Handler and Runnable members we defined and send our thread to sleep until the next Bitmap needs to be drawn.

new Thread(new Runnable() {
         public void run() {
            final int n = mGifDecoder.getFrameCount();
            final int ntimes = mGifDecoder.getLoopCount();
            int repetitionCounter = 0;
            do {
              for (int i = 0; i < n; i++) {
                 mTmpBitmap = mGifDecoder.getFrame(i);
                 final int t = mGifDecoder.getDelay(i);
                 mHandler.post(mUpdateResults);
                 try {
                    Thread.sleep(t);
                 } catch (InterruptedException e) {
                    e.printStackTrace();
                 }
              }
              if(ntimes != 0) {
                 repetitionCounter ++;
              }
           } while (mIsPlayingGif && (repetitionCounter <= ntimes))
        }
     }).start();

That’s it. All we have to do now is use our GifDecoderView in an Activity, just like we did in the last part of this tutorial:

   // ...
   InputStream stream = null;
   try {
      stream = getAssets().open("piggy.gif");
   } catch (IOException e) {
      e.printStackTrace();
   }

// GifMovieView view = new GifMovieView(this, stream);
   GifDecoderView view = new GifDecoderView(this, stream);

   setContentView(view);
   // ...

 

Now, what’ the bad thing about this? It’s the memory footprint. Bitmaps on pre-Honeycomb-devices are stored in an native heap, not in the heap the Dalvik VM uses. Still, this heap counts to the maximum of memory your app can use. So when you have a lot of Bitmaps, the garbage collector might not notice that you are running out of memory because it only controls the heap of the Dalvik VM. To avoid this, make sure you call recycle() on every Bitmap you don’t need anymore. If you want to see how much space your native heap allocated, call Debug.getNativeHeapAllocatedSize().

If you want to know another, maybe better way of playing animated GIFs, stay tuned: The next part of this series will come soon.

 

You can checkout the code of the three parts of this series at http://code.google.com/p/animated-gifs-in-android/.

 

As always, please feel free to leave your thoughts in the comments.

Tutorial: How to play animated GIFs in Android – Part 1

Animated GIFs in Android is a difficult topic. It is an issue that has been discussed heavily and still, it is very difficult for developers to bring GIFs to life. There are three ways to animate GIFs on Android, each of them has its pros and cons. Each part of this series will cover one of these approaches.

Getting started

For this example, we will use an image I found on gifs.net, it’s this one:

I will store it in our project’s asset folder and name it ‘piggy.gif’. We will also use an Activity to set the views we define as content views.

Approach 1: Using Movie

Android provides the class android.graphics.Movie. This class is capable of decoding and playing InputStreams. So for this approach, we create a class GifMovieView and let it inherit from View:

Public class GifMovieView extends View

Now we create a constructor that receives a Context object and an InputStream. We provide our class a member variable which is an instance of the Movie class. We initialize this member by calling Movie.decodeStream(InputStream):

    private Movie mMovie;

    public GifMovieView(Context context, InputStream stream) {
        super(context);

        mStream = stream;
        mMovie = Movie.decodeStream(mStream);        
    }

Now that our Movie-object is initialized with our InputStrem, we just need to draw it. We can do this by calling draw(Canvas, int, int) on our Movie-object. Because we need a Canvas, we should do this in onDraw(). But before we drawing, we have to tell our object what to render. For that, we need a simple calculation to determine how much time has passed since we started the Movie. To do that, we need another member of the primitive type long, I named it mMoviestart. Now we get a timestamp, for example by calling SystemClock.uptimeMillis() or System.currentTimeMillis(). We determine how much time went by since our movie started and tell our movie to play draw the according frame. After that, we invalidate our view so that it’s redrawn:

private long mMoviestart;

    @Override
    protected void onDraw(Canvas canvas) {
       canvas.drawColor(Color.TRANSPARENT);
       super.onDraw(canvas);
       final long now = SystemClock.uptimeMillis();
       if (mMoviestart == 0) {
          mMoviestart = now;
       }
       final int relTime = (int)((now - mMoviestart) % mMovie.duration());
       mMovie.setTime(relTime);
       mMovie.draw(canvas, 10, 10);
       this.invalidate();
    }

All we have to do now is initialize our new View with a Context and an InputStream and set it as content view, we can do this in our Activity like this:

    // ...
    InputStream stream = null;
    try {
       stream = getAssets().open("piggy.gif");
    } catch (IOException e) {
      e.printStackTrace();
    }
    GifMovieView view = new GifMovieView(this, stream);
    setContentView(view);
    // ...

That was easy, right? So where’s the contra? Well, here it is:

As you can see, the Movie-class is not able to deal with every type of animated GIFs. For some formats, the first frame will be drawn well but every other won’t. So when you walk this route, make sure your GIFs are displayed correctly.
If you want to know another, maybe better way of playing animated GIFs, stay tuned: The next part of this series will come tomorrow.

 

You can checkout the code of the three parts of this series at http://code.google.com/p/animated-gifs-in-android/.

 

As always, please feel free to leave your thoughts in the comments.

7 reasons why it’s great to be an app developer

When the iPhone and the first Android SDK hit the public back in 2007, the word ‘app’ was not very present. Today, everyone knows what an app is and more and more people are developing them. The markets are being flooded by new apps, every day about thousand of them are being published solely on the Android Market. Most developers don’t even get a chance to make a living by their apps because most times, there is a professional company doing a better job in shorter time because they have more resources. So why become an app developer? Just to try to be one of the few successful developers out of thousands? Here are 7 reasons why it’s great to be an app developer:

1. The users

When you are developing your own apps, and that is what most people do at the beginning, there comes a point when your app is released. The great thing now is that your app will probably reach more users than any other piece of software you have written ever has (A quick comparison: This blog, which is software too, gets about 5.000 impressions each month, the AL Voice Recorder gets about 750.000). And they will give you feedback. You will see whether your idea, implementation and enhancements will be well received or not pretty quickly. It’s really great to see how people are engaging in your software and help you to improve.

2. It’s highly agile

There is probably no other field in software development that’s as agile as app development is. The contact with the customer (either your users or the people that pay you) is tight, prototyping is rapid.

3. Ideas are welcome

Because the environment is highly agile, it is way more likely that your ideas will be listened to and implemented. In most cases, this is explicitly desired and most times the reason why you are getting a job or, if ideas are missing, not.

4. It’s an act of creation

Like most software development, app development is an act of creation. The difference lays in its agileness. Because of the fast prototyping, you can test and enjoy the result of your work really fast which is a great motivation.

5. There is room for try and error

When you don’t know how things work, you use try and error. That should happen in the beginning but become less and less as you advance on your field of development. Still, there is room for try and error when it comes to new features: When you don’t know whether your users will accept a new app or feature of yours and don’t have the time or money to do a survey, you simply publish it in the market or to your private beta testers. You will know whether your idea was an error really quick!

6. It’s fun

All the agility, the act creation, the room for own ideas and the user feedback result in one thing: App development is fun (most times)!

7. There is money

Because fun is great but doesn’t pay bills: Of course there’s money to make in app development. As long as your app doesn’t suck and your target audience is big enough, there’s no reason why your app shouldn’t be successful.

Conclusion

Personally, I think it’s useless to do things that are not great most of the times you do them. So to me and a lot other people, app development really is great and a great path to walk on.

What’s your path? Why do you think app development is great? Please feel free to tell us in the comments.

How to combine Madvertise with another ad network

A lot of people asked me how I implement the switch of Madvertise to another ad network. Here is how I do it:

I’m doing this example using an AdMob and an Madvertise view. Of course you can use any other similar networks like Adwhirl or Mobclix as well but I’m assuming you are familiar with including the two SDKs you are using.

At first we need a layout resource, here it is:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
  xmlns:mad="http://schemas.android.com/apk/res/com.andlabs.gi"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  > 
          <com.google.ads.AdView
            android:id="@+id/admad"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ads:adUnitId="y0ur4dun1t1dh3r3"
            ads:adSize="BANNER"
            ads:loadAdOnCreate="true"
            />
         <de.madvertise.dev.android.MadView
             android:id="@+id/madad"
             android:layout_width="match_parent"
             android:layout_height="Wrap_content"
             mad:isTestMode="false"
             mad:backgroundColor2="#000000"
             mad:textColor="#FFFFFF"
             mad:bannerType="mma"
             mad:deliverOnlyText="false"
             android:visibility="gone"     
             />                
</FrameLayout>

Pretty basic. A Madvertise and an AdMob view. Next step: Get them in your Activity:

 private MadView mMadView;
 private AdView mAdmView;
 @Override
     public void onCreate(Bundle bundle) {
          super.onCreate(bundle);
          /* ... */
          mAdmView = (AdView)findViewById(R.id.admad);
          mMadView = (MadView)findViewById(R.id.madad);
     }


That’s easy too. Next, register an MadViewCallbackListener and switch the views the way you need it. As you have seen the MadView is invisible at the beginning. I assume that Madvertise always brings in more money, so when I receive an ad by them, I set the AdMob view to invisible and the MadView to visible. That’s essentially it:

 mMadView.setMadViewCallbackListener(new MadViewCallbackListener() {
     @Override public void onLoaded(boolean success, MadView arg1) {
          if (success) {
               if(D) {
                    Log.d(this.getClass().getSimpleName(), "MadAd Received");
               }  
               if (mAdmView.getVisibility() == View.VISIBLE) {
                    mAdmView.setVisibility(View.GONE);
               }
               mMadView.setVisibility(View.VISIBLE);
          } else {
               if(D) {
                    Log.d(this.getClass().getSimpleName(), "No MadAd Received");
               }
               mMadView.setVisibility(View.GONE);
          }
     }
 });

Not a big deal. And that’s all. Now you have your own little meta ad network.

 

Please feel free to leave your experiences, questions or suggestions in the comments.

© 2025 Droid-Blog

Theme by Anders NorenUp ↑