This is the first of a series of posts about Fresco, the open-source library developed by Facebook for downloading and displaying images. We want to describe how easy it is to install and use Fresco in your Android application, creating a small app from scratch which simply loads and displays an image given its URL. The app we’ll develop is available here . Let’s start!

Creation of the Project

In order to describe the usage of Fresco we suppose to create an empty application like the one you can create with Android Studio Wizard or any other tool. In our example we have set minimum SDK version to Gingerbread (API Level 9) which is the oldest version of Android supported by Fresco. If you don’t need to support this version, you can choose a different one as the minimum and still have benefits from the library. At the beginning our application should look like this.

Figure 1 - The empty application as created from Android Studio or other IDE

Not a very interesting app, is it? Let’s add some components to the UI. Let’s open the layout document, remove the generated code if any, and replace it with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.facebook.fresco101.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/uri_edit_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_weight="1"
            android:hint="@string/uri_edit_text_hint"
            android:imeOptions="actionDone"
            android:singleLine="true"/>

        <Button
            android:id="@+id/clear_uri"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/clear_uri"/>
    </LinearLayout>

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

This is a very simple layout which displays an EditText on the top, where we’ll insert the URL of an image to load and display, along with a Button in order to clear it. Below these components we simply have an ImageView to display the downloaded Bitmap. We’ll write more blogs about this but a Bitmap is basically the Android object representing an image. This will look like the picture below:

Figure 2 - Our simple layout with EditText, Button and ImageView

We still have to add some logic. When the user types something into the EditText, we have to check if it’s a valid URI and if so, download and display the Bitmap into the ImageView. Our first attempt sends us to code like this in the MainActivity class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.*activity_main*);
  mImageView = (ImageView) findViewById(R.id.*image_view*);
  // Events on EditText
  final EditText editText = (EditText) findViewById(R.id.uri_edit_text);
  editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override    
    public boolean onEditorAction(TextView view, int actionId, KeyEvent keyEvent) {
      final boolean isEnterKeyDown = (actionId == EditorInfo.IME_NULL) &&
                                             (keyEvent.getAction() == KeyEvent.ACTION_DOWN);
      if (isEnterKeyDown || actionId == EditorInfo.IME_ACTION_DONE) {
        updateImageView(Uri.*parse*(view.getText().toString()));
      }
      return false;
    }
  });
  findViewById(R.id.clear_uri)
          .setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              editText.getText().clear();
            }
          });
}

The code which interests us is the one in bold, which just wants to fetch the Bitmap. Our updateImageView() implementation is like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private void updateImageView(Uri uri) {
  AsyncTask<Uri, Void, Bitmap> task = new AsyncTask<Uri, Void, Bitmap>() {
    @Override
    protected Bitmap doInBackground(Uri... uris) {
      InputStream inputStream = null;
      Bitmap bitmap = null;
      try {
        bitmap = fetchBitmap(uris[0]);
      } catch (Exception e) {
        // Head in the sand!
      } finally {
        if (inputStream != null) {
          try {
            inputStream.close();
          } catch (IOException e) {
            // Head in the sand!
          }
        }
      }
      return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
      super.onPostExecute(bitmap);
      if (bitmap != null) {
        mImageView.setImageBitmap(bitmap);
      } else {
        if (Build.VERSION.*SDK_INT *>= Build.VERSION_CODES.LOLLIPOP) {
          mImageView.setImageDrawable(getResources().getDrawable(R.drawable.error_icon, getTheme()));
        } else {
          mImageView.setImageDrawable(getResources().getDrawable(R.drawable.error_icon));
        }
      }
    }
  };
  task.execute(uri);
}

The code is not very clean and there are also different problems that we can list here:

  • We cannot access the network in the UI Thread, so we have to use an AsyncTask or equivalent component
  • Network access can fail, so we have to deal with different Exceptions
  • We have to implement the logic related to the display of a possible error. In our case we also have to distinguish between different API levels.

In our code the fetchBitmap() method is implemented in very basic way like this:

1
2
3
4
5
6
7
8
9
10
11
private Bitmap fetchBitmap(Uri uri) throws IOException {
  Bitmap bitmap = null;
  try {
    final URL url = new URL(uri.toString());
    InputStream urlConnection = url.openStream();
    bitmap = BitmapFactory.decodeStream(urlConnection);
  } catch (Exception e) {
    e.printStackTrace();
  }
  return bitmap;
}

Beside the implementation detail there’s also other problems:

  • Are we sure our Bitmap is a network image, needing a network connection in order to fetch it? Could it be a local resource? What if comes from a ContentProvider?
  • If we ask for the same URI more than once we have to access the network twice. The same if we just rotate the device. We’re missing any caching logic. Sure, we could add here but it would add other code to what we simply want: display an image given its URI.
  • One of the most important resource in most of the devices is memory, and Bitmaps are probably the largest objects in memory. In order to prevent the hated OutOfMemoryError (OOMs) we need to manage memory in an efficient and transparent way.

In order to prove that we can just run the application and try to display some images from the network. Before testing the app we don’t have to forget to add the INTERNET permission into AndroidManifest.xml:

1
<uses-permission android:name="android.permission.INTERNET"/>

Then we run the app and use the adb tool in order to send a URI to the app and press Enter to generate the fetching event. If we run the application and use this command:

adb shell input text "https://raw.githubusercontent.com/facebook/fresco/master/docs/static/sample-images/transparent_png.png"

we can see the same URI into the EditText and so the image after we confirm with enter on the keyboard. In this case the result is the one in Figure 3.

Figure 3 - The image of the given Url

In order to fix all the problems listed before we need something more that some lines of code and so a framework which allows us to fetch any type of images taking care of the error management and, more important, memory management; this is Fresco.

The Fresco way

After the description of a self-made application for downloading and display images we want to see how to use Fresco. The first step is to add the related dependency to the build.gradle file like this:

1
2
3
4
5
6
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.facebook.fresco:fresco:0.13.0'
}

As we can see the current release is the 0.13 and contains all the Fresco core APIs. The next step is related to the change of the layout that we put into a different file called fresco_activity_main.xml that differs from the previous only for this definition in place of the ImageView:

1
2
3
4
5
6
7
<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/simple_drawee_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:actualImageScaleType="fitCenter"
    app:failureImage="@drawable/error_icon"
    />

As we can see we’re using a Custom View defined by the class SimpleDraweeView. It’s a simple realization of what it’s called Drawee and it will be topic of a future post. As we’ll see there, a Drawee is a more complex and complete concepts but in this case it’s basically a custom View which allows us to:

  • Be a smart replacement for the ImageView delegating most of the drawing to a composition of Drawable which are much lighter of a View
  • Interact in efficient way with the ImagePipeline responsible of the fetching and caching of the requested Bitmap
  • Provide, in declarative and imperative way, of several features like
    • Rounding corners
    • Fading and other effects
    • Progress bars
    • Overlay
    • Customized ScaleType
    • More…

For instance, in the previous example we’re saying, in declarative way, that we want to display a red square(or any other Drawable) in case of error. There are many others attribute as we can see here. After this we create a different Activity called FrescoMainActivity whose code is like this where we have put in bold the difference from the previous code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class FrescoMainActivity extends AppCompatActivity {

  private SimpleDraweeView mDraweeView;**

  @Override  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fresco_activity_main);
    mDraweeView = (SimpleDraweeView) findViewById(R.id.simple_drawee_view);
    // Events on EditText
    final EditText editText = (EditText) findViewById(R.id.uri_edit_text);
    editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
      @Override
      public boolean onEditorAction(TextView view, int actionId, KeyEvent keyEvent) {
        final boolean isEnterKeyDown = (actionId == EditorInfo.IME_NULL) &&
                                               (keyEvent.getAction() == KeyEvent.ACTION_DOWN);
        if (isEnterKeyDown || actionId == EditorInfo.IME_ACTION_DONE) {
          updateImageView(Uri.parse(view.getText().toString()));
        }
        return false;
      }
    });
    findViewById(R.id.clear_uri)
            .setOnClickListener(new View.OnClickListener() {
              @Override              
              public void onClick(View v) {
                editText.getText().clear();
              }
            });
  }

  private void updateImageView(Uri uri) {
    DraweeController controller = Fresco.newDraweeControllerBuilder()
                                          .setUri(uri)
                                          .build();
    mDraweeView.setController(controller);
  }
}

Beside the usage of the SimpleDraweeView we have implemented the updateImageView() in a very simple way using what we have called DraweeController. In this code we basically create an instance of the DraweeController using a Builder that, in the simplest case, just use the Uri of the Bitmap to display. The magic happens when we set the Controller to the DraweeView. Before testing the app we need to do another step. Fresco needs to initialize some objects in order to provide its feature. This must be done only once during the application lifecycle and so the best place is into an Application implementation like this:

1
2
3
4
5
6
7
8
public class Fresco101Application extends Application {

  @Override  
  public void onCreate() {
    super.onCreate();
    Fresco.initialize(this);
  }
}

As we can see it’s a super simple class which uses the initialize() static method in its simplest overload. Before to go a little in more detail let’s set this Activity as launch in the AndroidManifest.xml configuration file, add the previous Application class and finally run the application which will load the image like before with displaying it with a small fade effect provided by Drawee.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?><manifest package="com.facebook.fresco101"
                xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:name=".Fresco101Application"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".FrescoMainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

As before we can insert an Url using the adb command and see it displayed into the DraweeView as in Figure 9 but using Fresco we implicitly have many other features that will be topic of our next posts.

Figure 4 - The image of the given Url with Fresco

As we have seen into the previous code, the Fresco architecture is based on a classic Model View Controller (MVC) where the View is our DraweeView, the Model is what we have provided through the ImagePipeline and the Controller is our DraweeController which is responsible to make the DraweeView communicate with the ImagePipeline in efficient way. Each of these components have many options that the developer can customize in order to add new feature or tune the existing ones. But Fresco is much more as we’ll se in the next section.

What about other image formats?

In the “self-made” version of the Fresco101 application we could just load images from the Network. With fresco we can also access images from the local ContentProvider (the Photos), local files or resources. To do this we just need to set the proper Uri. As said we’ll see all this in a future post. Here we want to see another dimension of the problem depending on the format of the image we want to fetch. The previous example is related to a PNG image. In order to test our application let’s try to fetch and display a JPEG image using this command:

adb shell input text "https://www.gstatic.com/webp/gallery/1.sm.jpg"

and so displaying a jpg image as in Figure 5: Figure 5 - We display a static JPEG image

This is easy; it’s just a JPEG right? What about if the JPEG is a progressive JPEG. A progressive JPEG (https://en.wikipedia.org/wiki/JPEG) is a format which allows to render the image while downloading it and so without waiting for its completion. Our previous “naive” version of the application doesn’t manage Progressive JPEG as it is but Fresco does. In order to do so we can use a specific Uri which references a progressive JPEG image whose fetching is very slow (on purpose). In order to manage progressive JPEG with Fresco we have to slightly change the code we use in order to build the DraweeController which now is like this:

1
2
3
4
5
6
7
8
9
private void updateProgressiveImage(Uri uri) {
  ImageRequest request = ImageRequestBuilder.newBuilderWithSource*(uri)
                                 .setProgressiveRenderingEnabled(true)
                                 .build();
  DraweeController controller = Fresco.newDraweeControllerBuilder()
                                        .setImageRequest(request)
                                        .build();
  mDraweeView.setController(controller);
}

In this case we need to set more informations about our request so we create, from the given Uri, an instance of ImageRequest setting the option related to progressive rendering. Then we create the DraweeController using the Builder but passing to it the ImageRequest. Now we can test the application using this command:

adb shell input text "http://pooyak.com/p/progjpeg/jpegload.cgi?o=1"

In this case we can see in Figure 6 that a first blurred version of the image is loaded and then the same image with better resolution (progressive). Figure 6 - Progressive JPG with Fresco in Action - First scan Figure 6 - Progressive JPG with Fresco in Action - Intermediate scan Figure 6 - Progressive JPG with Fresco in Action - Last scan

With the same method we can manage also GIF and WEBP formats. The WEBP format is very efficient and is already supported natively on Android from the version 4.2.1. Our application has a minimum Api Level of 9 which doesn’t support WEBP natively. Because this format is something not all application need, we have created an optional module. This means that if you don’t have to support WEBP for Android < 4.2.1 you don’t have to add what we have called static-webp module. In case you simply have to add the webpsupport dependency to the gradle.build file:

1
2
3
4
5
6
7
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.facebook.fresco:fresco:0.13.0'
    compile 'com.facebook.fresco:webpsupport:0.13.0'
}

With this you can load a WEBP image also on devices which don’t support this format natively and so test the app with this Uri

adb shell input text "https://www.gstatic.com/webp/gallery/2.sm.webp"

or this in case of WEBP with transparency:

adb shell input text "http://frescolib.org/static/translucent.webp"

We used 2 different Uris because we manage WEBP on old devices using a process called transcoding. In short we transcode any WEBP with transparency into a PNG and without transparency into a JPEG image. We also have an experiment ongoing that can be enabled using the related methods in the ImagePipelineConfig like in this code:

1
2
3
4
5
6
7
8
9
10
11
**public class **Fresco101Application **extends **Application {

  @Override  public void onCreate() {
    super.onCreate();
    ImagePipelineConfig config = ImagePipelineConfig.*newBuilder(this)
                                 .experiment().setWebpSupportEnabled(true)
                                 .experiment().setDecodeFileDescriptorEnabled(true)
                                 .build();
    Fresco.initialize(this, config);
  }
}

What about Animation?

Previously we have said that Fresco supports different formats included GIF. What about animated GIF? Also in this case we understand that not all applications need this feature so we have created an optional module that we can include in our project just adding these lines to our gradle.build file:

1
2
3
4
5
6
7
8
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.facebook.fresco:fresco:0.13.0'
    compile 'com.facebook.fresco:webpsupport:0.13.0'
    compile 'com.facebook.fresco:animated-gif:0.13.0'
}

Animations need to be played so also in this case we need a little change in the way we create the DraweeController like this:

1
2
3
4
DraweeController controller = Fresco.newDraweeControllerBuilder()
                                      .setImageRequest(request)
                                      .setAutoPlayAnimations(true)
                                      .build();

If we’re not directly accessing Animation API we use the setAutoPlayAnimations() in order to start the animation as soon as it’s been loaded. If we want to test this feature we can just use this command:

adb shell input text "https://s3.amazonaws.com/giphygifs/media/4aBQ9oNjgEQ2k/giphy.gif"

and se the result in Figure 7 which will be animated: Figure 7 - Animated GIF

If we also want to display animated WEBP we can do this just adding this dependency:

1
2
3
4
5
6
7
8
9
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.facebook.fresco:fresco:0.13.0'
    compile 'com.facebook.fresco:webpsupport:0.13.0'
    compile 'com.facebook.fresco:animated-gif:0.13.0'
    compile 'com.facebook.fresco:animated-webp:0.13.0'
}

and finally test the application with this command:

adb shell input text "https://www.gstatic.com/webp/animated/1.webp"

Conclusion

In this Blog post we have seen how Fresco can be useful when you have to deal with images. At the beginning we have created a simple “naive” application in order to load an image given a URL. After some code we have seen that also a simple application like that should consider many aspects like error management, caching, memory and many others that require a complete library like Fresco. We have seen how to integrate Fresco into an existing projects and how to use it for image loading. In particular we have seen how to load images of different formats: progressive JPEG and animated GIF included. In the following posts we’ll see more details of the Framework but this is for sure a good start.