We can imagine that a fair share of iPads got unwrapped this morning, and the first thing you’re going to want to do is switch it on and get a few awesome apps on there. Last year, we gave you a long list of apps that will help you get started with all of the popular essentials.
This year, we’ve decided to do the same thing, adding a ton of great apps to the list that you’ll want to get on to your iPad straight away. The list includes apps to handle your photos and videos, music apps, productivity apps, a few essentials to meet all of your social media needs, and of course a few games to keep you entertained.
Media
Snapseed - If you’re a photography buff and are finally going to get that iPad you always wanted, we’d recommend getting yourself an iPad SD Card Reader from Photojojo. This makes it easier than ever to get your photos straight from your camera onto your iPad, and it’s half the price of Apple’s iPad Camera Connection Kit. Once the photos are transferred, you’re going to want to be able to edit, process and share your photos. The best app we’ve come across that does this is Snapseed, which we’ve previously reviewed here. The $4.99 app is worth every cent. It comes with easy-to-use autocorrect features which will adjust your images at the click of a button, or if you want more control over your image’s final look, you can manually adjust basics like sharpness, brightness and contrast, and use some pretty cool looking filters to give your photos a grunge or vintage look. Snapseed is controlled entirely by swiping your image either right to left or up and down.
Pandora - For streaming random music or personalized radio stations, Pandora is a great option for those of you who are lucky enough to live in the right country. The iPad app makes it easy to access all of your Pandora radio stations when you log in, and you can rate, skip and pause the music.
MusicTandem - For those of you who don’t have access to Pandora, check out the $0.99 app MusicTandem, which we reviewed here. With the app, you can create personalized radio stations based on artist, genre or tag. When selecting music based on a specific artist, you can choose to play music only by that singer or band, or to play a variety of music similar to that one artist. Our one complaint when it comes to MusicTandem is that it doesn’t play in the background which is a much-needed feature. Another music app which certainly deserves an honourable mention for discovering new music is MusicHunter, which we’ve reviewed in-depth here, but it’s worth noting that the app is now completely free.
Shazam - Another essential app for music buffs is Shazam. The free app comes with unlimited tagging, so if you hear a song on the radio or TV and want to know what it is, just whip out your new iPad, hit tag, and let the magic of Shazam do the rest for you.
Showyou - If you want to watch online videos on your iPad, a great way to do that is with the the free app Showyou. You can connect the app to your YouTube, Vimeo, Twitter, Facebook and Tumblr accounts, making it easy to find interesting videos, and instantly share them with your friends and followers. Showyou is not only fully searchable if you’re looking for a specific video to watch, but it also comes pre-loaded with grids featuring videos on various topics and from various sources, with everything from Al Jazeera to Reddit TV. If you want to save a video to watch later, all you have to do is sign up for a free account.
FlexPlayer – Since VLC was pulled from the iTunes App Store, the best free alternative available now is FlexPlayer. You don’t have to convert your videos before transferring them to your iPad. Just hook your iPad up to your computer, fire up iTunes and copy your movie files to your iPad. FlexPlayer is a slick video player, and add to that the fact that it’s ad-free, there’s really nothing else that you need to watch your movies and TV shows on your iPad.
Social Media
Zite - We’re big fans of Zite here at The Next Web, and reviewed it when it first launched here. Zite is one of the best ways to get your daily news fix with minimal effort on your part. All you have to do is plug in your preferred topics and the app will do the rest for you. And the more you use it, the better it becomes at making recommendations based on your personal preferences. Zite’s UI is best bar none. It’s sleek and minimal, showcases the articles you’re reading beautifully, and makes it easy to share posts on your favourite social networks or save them for reading later.
Blogsy - You might not get as much writing as you’d expect using the iPad, but if you’re really serious about it, investing in a bluetooth keyboard to go alongside your iPad will make life much easier. So which apps should you download if you’re serious about your writing? The first app a blogger will probably want to download is Blogsy. The $4.99 app supports WordPress, Blogger and Posterous, while also allowing you to access your Flickr, Picasa and YouTube accounts. Blogsy makes it easy to write up and share media-rich posts straight from your iPad, allowing you to upload images and drag-and-drop videos, and also comes with text formatting.
Verbs - Verbs is a pretty slick chat app for the iPad with Google Talk, AIM, Facebook and MobileMe support, and allows you to add multiple accounts. For easy photo-sharing you can also connect your Cloud or Droplr accounts. Normally a paid app, you can get it free for now, but the free version does have its limits. The paid upgrade at $4.99 will get you push notifications, whereas the free version will only notify you of messages for the first 10 minutes that the app runs in the background.
StumbleUpon - When StumbleUpon finally launched an updated iPad app this year, one of our observations when reviewing it was that the iPad was made for this kind of site. And the app certainly lives up to that. We should warn you though, if you’re a fan of StumbleUpon, installing this app on your new iPad will suck you right in and make it hard to put your iPad down.
PhotoSync - For all of your photo uploading needs, rather than download a different app for each account, PhotoSync supports Dropbox, Picasa, Facebook, SmugMug and Flickr. Select multiple photos to upload simultaneously to all of your accounts. You have extensive control over how each account is configured – from privacy settings, folders and more.
500px - Want to browse gorgeous photos on your brand new iPad? Then the first app you need to download is the official 500px iPad app, which we reviewed here. The quality of photography on 500px is pretty impressive, and where better to check it out than on the iPad. If you have a 500px account, you can log in to follow other users and add their photos to your favourites, but unfortunately, you can’t actually upload any images to the site from your iPad using this free app. If you want that capability, you’re going to have to opt for the $0.99 app, PhotoStackr for 500px.
Productivity
Polkast - If you prefer not to have to upload files to the cloud, Polkast is a great alternative. The only drawback is that you’ll have to stay logged in to your computer in order to access your files. On the other hand, there are no complicated settings to fiddle with in order to get the connection to work. Simply install the app on your Mac or Windows computer, install the app on your iPad, and you’re good to go. You can play videos and music straight from the app, or download the files to your iPad. If you modify files on your iPad, you can save the new file back to your computer right from your iPad.
Wunderlist - If you’re looking for a multiplatform productivity app to keep track of your task list, look no further than Wunderlist. The app is completely free regardless of which OS you use, and also syncs to the cloud, so you’ll be sure never to lose your task list, and have access to it on all of your mobile devices and on your computer as well. It’s no surprise the app has over 1 million users now.
iA Writer – While Blogsy has you covered for most of your online writing needs, if you need something to save notes locally on your iPad, we’ll tell you right now, the native notes app that the iPad ships with simply isn’t going to cut it. The $1.99 app iA Writer is a great option because it comes packed with some pretty decent features without locking your content on your iPad. You can sync all of your content on the distraction-free iPad writing app with its Mac counterpart, using Dropbox or iCloud. For those of you who don’t use Macs, you can simply take advantage of the Dropbox backup to get your documents from your iPad to your computer.
Switch - If you know for a fact that your new iPad is going to become a form of entertainment for the whole family, you’ll be disappointed to find that you can’t password protect your apps or create user accounts. The closest you’ll come is to use the $4.99 app Switch which allows you to create browser-related accounts. With Switch, each user can password protect their browsing, bookmarks and logged-in accounts. Of course this applies only to the browser app and doesn’t affect any other apps on your iPad.
Utilities
Flip Clock HD - Your new iPad is going to look great sitting on your bedside table so you might as well put it to good use. With Flip Clock HD you can turn your iPad into a stylish clock which not only lets you keep track of the time and date, but you can also check out your local weather at a glance. Using the app as an alarm clock will give you the option of waking up to tracks on your iPad. When using the app as an alarm clock you will have to keep it open, since it doesn’t work in the background, but the screen does dim if you leave it running without touching the screen.
Skitch - If you’ve been using Skitch on your Mac, you’ll be happy to know that it’s available on the iPad as well. The free app is the best option available for annotating your images and screenshots and instantly save them to your Evernote account.
Games
The Sims 3 - If you’re looking for a game to keep you busy for endless hours, Sims might be the right game for you. The best part is that EA has made a really decent free version available so you can try it out first. Although right now the full version is available for just $0.99 so get it on sale while you can.
Temple Run - Before downloading Temple Run, be warned. It’s extremely addictive. The current version of the free iPad game has been rated over 55,000 times and is standing strong on a 5 star rating. And for good reason. The 3D adventure will have you making your way through a maze, collecting coins, and swiping left and right in the high speed game.
Machinarium - You might be surprised to find that the $4.99 app Machinarium, which only works on the iPad 2, was created using Adobe Flash. The game has received rave reviews, and with the amazing graphics and music, you’ll understand why. The game is a gorgeous experience of puzzles slipped into a point-and-click adventure, and makes for a fun and challenging experience if you have a bit of time to kill.
The Next Web
Of course, we also recommend The Next Web’s iPad app which makes it easy to keep up with all the latest tech news, using a beautiful app designed specifically for Apple’s tablet.
What are your favourite must-have iPad apps? Let us know in the comments.
Hibernate was started in 2001 by Gavin King as an alternative to using EJB2-style entity beans. Its mission back then was to simply offer better persistence capabilities than offered by EJB2 by simplifying the complexities and allowing for missing features.
Early in 2003, the Hibernate development team began Hibernate2 releases which offered many significant improvements over the first release.
JBoss, Inc. (now part of Red Hat) later hired the lead Hibernate developers and worked with them in supporting Hibernate. Hibernate is part of JBoss (a division of Red Hat) Enterprise Middleware System (JEMS) suite of products.
1. Introduction
Hibernate is an Object-relational mapping (ORM) tool. Object-relational mapping or ORM is a programming method for mapping the objects to the relational model where entities/classes are mapped to tables, instances are mapped to rows and attributes of instances are mapped to columns of table.
A “virtual object database” is created that can be used from within the programming language.
Hibernate is a persistence framework which is used to persist data from Java environment to database. Persistence is a process of storing the data to some permanent medium and retrieving it back at any point of time even after the application that had created the data ended.
2. Hibernate Architecture
The above diagram shows minimal architecture of Hibernate. It creates a layer between Database and the Application. It loads the configuration details like Database connection string, entity classes, mappings etc.
Hibernate creates persistent objects which synchronize data between application and database.
The above diagram shows a comprehensive architecture of Hibernate. In order to persist data to a database, Hibernate create an instance of entity class (Java class mapped with database table). This object is called Transient object as they are not yet associated with the session or not yet persisted to a database. To persist the object to database, the instance of SessionFactory interface is created. SessionFactory is a singleton instance which implements Factory design pattern. SessionFactory loads hibernate.cfg.xml file (Hibernate configuration file. More details in following section) and with the help of TransactionFactory and ConnectionProvider implements all the configuration settings on a database.
Each database connection in Hibernate is created by creating an instance of Session interface. Session represents a single connection with database. Session objects are created from SessionFactory object.
Hibernate also provides built-in Transaction APIs which abstracts away the application from underlying JDBC or JTA transaction. Each transaction represents a single atomic unit of work. One Session can span through multiple transactions.
2.1 SessionFactory (
org.hibernate.SessionFactory)A thread-safe, immutable cache of compiled mappings for a single database. A factory for org.hibernate.Session instances. A client of org.hibernate.connection.ConnectionProvider. Optionally maintains a second level cache of data that is reusable between transactions at a process or cluster level.
2.2 Session (
org.hibernate.Session)A single-threaded, short-lived object representing a conversation between the application and the persistent store. Wraps a JDBC
java.sql.Connection. Factory for org.hibernate.Transaction. Maintains a first level cache of persistent the application’s persistent objects and collections; this cache is used when navigating the object graph or looking up objects by identifier.2.3 Persistent objects and collections
Short-lived, single threaded objects containing persistent state and business function. These can be ordinary JavaBeans/POJOs. They are associated with exactly one
org.hibernate.Session. Once the org.hibernate.Session is closed, they will be detached and free to use in any application layer (for example, directly as data transfer objects to and from presentation).2.4 Transient and detached objects and collections
Instances of persistent classes that are not currently associated with a org.hibernate.Session. They may have been instantiated by the application and not yet persisted, or they may have been instantiated by a closed org.hibernate.Session.
2.5 Transaction (
org.hibernate.Transaction)(Optional) A single-threaded, short-lived object used by the application to specify atomic units of work. It abstracts the application from the underlying JDBC, JTA or CORBA transaction. A org.hibernate.Session might span several org.hibernate.Transactions in some cases. However, transaction demarcation, either using the underlying API or org.hibernate.Transaction, is never optional.
2.6 ConnectionProvider (
org.hibernate.connection.ConnectionProvider)(Optional) A factory for, and pool of, JDBC connections. It abstracts the application from underlying javax.sql.DataSource or java.sql.DriverManager. It is not exposed to application, but it can be extended and/or implemented by the developer.
2.7 TransactionFactory (
org.hibernate.TransactionFactory)(Optional) A factory for org.hibernate.Transaction instances. It is not exposed to the application, but it can be extended and/or implemented by the developer.
3. Hibernate Configuration
Hibernate configuration is managed by an instance of
org.hibernate.cfg.Configuration. An instance of org.hibernate.cfg.Configuration represents an entire set of mappings of an application’s Java types to an SQL database. The org.hibernate.cfg.Configuration is used to build an immutableorg.hibernate.SessionFactory. The mappings are compiled from various XML mapping files or from Java 5 Annotations.Hibernate provides following types of configurations
- hibernate.cfg.xml – A standard XML file which contains hibernate configuration and which resides in root of application’s CLASSPATH
- hibernate.properties – A Java compliant property file which holds key value pair for different hibernate configuration strings.
- Programmatic configuration – This is the manual approach. The configuration can be defined in Java class.
3.1 hibernate.cfg.xml
This is an alternate way of configuring hibernate. The hibernate.cfg.xml file is a standard XML file which contains all the configuration parameters like database connection, class mappings etc. This file needs to be placed root of CLASSPATH of application.
Below is the sample hibernate.cfg.xml file:
0102030405060708091011121314151617181920212223242526272829303132<?xmlversion='1.0'encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><hibernate-configuration><!-- a SessionFactory instance listed as /jndi/name --><session-factoryname="java:hibernate/SessionFactory"><!-- properties --><propertyname="connection.datasource">java:/comp/env/jdbc/MyEmployeeDB</property><propertyname="dialect">org.hibernate.dialect.MySQLDialect</property><propertyname="show_sql">false</property><propertyname="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property><propertyname="jta.UserTransaction">java:comp/UserTransaction</property><!-- mapping files --><mappingresource="net/viralpatel/hibernate/Employee.hbm.xml"/><mappingresource="net/viralpatel/hibernate/Department.hbm.xml"/><!-- cache settings --><class-cacheclass="net.viralpatel.hibernate.Employee"usage="read-write"/><class-cacheclass="net.viralpatel.hibernate.Department"usage="read-only"/><collection-cachecollection="net.viralpatel.hibernate.Department.employees"usage="read-write"/></session-factory></hibernate-configuration>Once the hibernate.cfg.xml file is created and placed in root of application’s CLASSPATH, the same can be loaded in Hibernate using following API.
1SessionFactory sessionFactory =newConfiguration().configure().buildSessionFactory();This above code will load default hibernate.cfg.xml file and all the configuration mentioned in it.
In case you want to override default naming convention and want to have your own configuration file like “employeedb.cfg.xml”, following API can be used:
123SessionFactory sf =newConfiguration().configure("employeedb.cfg.xml").buildSessionFactory();
Note: Both hibernate.cfg.xml and hibernate.properties files can be provided simultaneously in an application. In this case hibernate.cfg.xml gets precedence over hibernate.properties.
3.2 hibernate.properties
This is the easiest way to get started with Hibernate. Create a file hibernate.properties and place it in root of your applications CLASSPATH.
Below is the sample hibernate.properties file:
01020304050607080910hibernate.connection.driver_class=com.mysql.jdbc.Driverhibernate.connection.url= jdbc:mysql://localhost:3306/employeehibernate.connection.username=roothibernate.connection.password=swordfishhibernate.connection.pool_size=1hibernate.transaction.factory_class = \org.hibernate.transaction.JTATransactionFactoryhibernate.transaction.manager_lookup_class = \org.hibernate.transaction.JBossTransactionManagerLookuphibernate.dialect = org.hibernate.dialect.MySQLDialectFor detail description of Configuration parameters, refer this article Hibernate configuration properties.
3.3 Programmatic configuration
We can obtain a org.hibernate.cfg.Configuration instance by instantiating it directly and specifying XML mapping documents. If the mapping files are in the classpath, use addResource(). For example:
123Configuration cfg =newConfiguration().addResource("Employee.hbm.xml").addResource("Department.hbm.xml");An alternative way is to specify the mapped class and allow Hibernate to find the mapping document for you:
123Configuration cfg =newConfiguration().addClass(net.viralpatel.hibernate.Employee.class).addClass(net.viralpatel.hibernate.Department.class);Hibernate will then search for mapping files named /net/viralpatel/hibernate/Employee.hbm.xml and /net/viralpatel/hibernate/Department.hbm.xml in the classpath. This approach eliminates any hardcoded filenames.
A org.hibernate.cfg.Configuration also allows you to specify configuration properties. For example:
123456Configuration cfg =newConfiguration().addClass(net.viralpatel.hibernate.Employee.class).addClass(net.viralpatel.hibernate.Department.class).setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect").setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test").setProperty("hibernate.order_updates","true");4. Building a SessionFactory
Once the instance of org.hibernate.cfg.Configuration is created using any of the above method, the singleton instance of SessionFactory can be created as follow:
1SessionFactory sessions = cfg.buildSessionFactory();Hibernate does allow your application to instantiate more than one org.hibernate.SessionFactory. This is useful if you are using more than one database.
5. Getting Session instance
As noted above, Session represents a communication channel between database and application. Each session represents a factory of transactions. Session can be created from SessionFactory as follows:
1Session session = sessions.openSession();// get a new SessionThus, in this article we saw an overview of Hibernate ORM and its architecture. Also we noted its different components like SessionFactory, TransactionFactory, Session etc and APIs to instantiate these objects in your application.
In next tutorial, We will write a Hello World Hibernate program using both XML file based configuration and Annotations.
Stay tuned!
![]()
See also:
- Hibernate One-to-one Mapping Tutorial using Annotation
- Hibernate hello world example using Annotation
- Hibernate Maven MySQL hello world example (XML Mapping)
- Hibernate One-to-one mapping tutorial using XML Mapping
- Tutorial:Saving/Retreving BLOB object in Spring 3 MVC and Hibernate
- Tutorial:Create Spring 3 MVC Hibernate 3 Example using Maven in Eclipse
- Writing a URL Shortner in Java Struts2 & Hibernate
- Tutorial: Create Struts2 Hibernate Example in Eclipse
[This post is by Gilles Debunne, an engineer in the Android group who loves to get multitasked. — Tim Bray]
A good practice in creating responsive applications is to make sure your main UI thread does the minimum amount of work. Any potentially long task that may hang your application should be handled in a different thread. Typical examples of such tasks are network operations, which involve unpredictable delays. Users will tolerate some pauses, especially if you provide feedback that something is in progress, but a frozen application gives them no clue.
In this article, we will create a simple image downloader that illustrates this pattern. We will populate a ListView with thumbnail images downloaded from the internet. Creating an asynchronous task that downloads in the background will keep our application fast.
An Image downloader
Downloading an image from the web is fairly simple, using the HTTP-related classes provided by the framework. Here is a possible implementation:
static Bitmap downloadBitmap(String url) { final AndroidHttpClient client = AndroidHttpClient.newInstance("Android"); final HttpGet getRequest = new HttpGet(url); try { HttpResponse response = client.execute(getRequest); final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url); return null; } final HttpEntity entity = response.getEntity(); if (entity != null) { InputStream inputStream = null; try { inputStream = entity.getContent(); final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); return bitmap; } finally { if (inputStream != null) { inputStream.close(); } entity.consumeContent(); } } } catch (Exception e) { // Could provide a more explicit error message for IOException or IllegalStateException getRequest.abort(); Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString()); } finally { if (client != null) { client.close(); } } return null;}A client and an HTTP request are created. If the request succeeds, the response entity stream containing the image is decoded to create the resulting Bitmap. Your applications' manifest must ask for the
INTERNETto make this possible.Note: a bug in the previous versions of
BitmapFactory.decodeStreammay prevent this code from working over a slow connection. Decode a newFlushedInputStream(inputStream)instead to fix the problem. Here is the implementation of this helper class:static class FlushedInputStream extends FilterInputStream { public FlushedInputStream(InputStream inputStream) { super(inputStream); } @Override public long skip(long n) throws IOException { long totalBytesSkipped = 0L; while (totalBytesSkipped < n) { long bytesSkipped = in.skip(n - totalBytesSkipped); if (bytesSkipped == 0L) { int byte = read(); if (byte < 0) { break; // we reached EOF } else { bytesSkipped = 1; // we read one byte } } totalBytesSkipped += bytesSkipped; } return totalBytesSkipped; }}This ensures that skip() actually skips the provided number of bytes, unless we reach the end of file.
If you were to directly use this method in your ListAdapter's getView method, the resulting scrolling would be unpleasantly jaggy. Each display of a new view has to wait for an image download, which prevents smooth scrolling.
Indeed, this is such a bad idea that the AndroidHttpClient does not allow itself to be started from the main thread. The above code will display "This thread forbids HTTP requests" error messages instead. Use the DefaultHttpClient instead if you really want to shoot yourself in the foot.
Introducing asynchronous tasks
The
AsyncTaskclass provides one of the simplest ways to fire off a new task from the UI thread. Let's create anImageDownloaderclass which will be in charge of creating these tasks. It will provide adownloadmethod which will assign an image downloaded from its URL to anImageView:public class ImageDownloader { public void download(String url, ImageView imageView) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); task.execute(url); } } /* class BitmapDownloaderTask, see below */}The
BitmapDownloaderTaskis the AsyncTask which will actually download the image. It is started usingexecute, which returns immediately hence making this method really fast which is the whole purpose since it will be called from the UI thread. Here is the implementation of this class:class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { private String url; private final WeakReference<ImageView> imageViewReference; public BitmapDownloaderTask(ImageView imageView) { imageViewReference = new WeakReference<ImageView>(imageView); } @Override // Actual download method, run in the task thread protected Bitmap doInBackground(String... params) { // params comes from the execute() call: params[0] is the url. return downloadBitmap(params[0]); } @Override // Once the image is downloaded, associates it to the imageView protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null) { ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } }}The
doInBackgroundmethod is the one which is actually run in its own process by the task. It simply uses thedownloadBitmapmethod we implemented at the beginning of this article.
onPostExecuteis run in the calling UI thread when the task is finished. It takes the resulting Bitmap as a parameter, which is simply associated with the imageView that was provided todownloadand was stored in theBitmapDownloaderTask. Note that this ImageView is stored as aWeakReference, so that a download in progress does not prevent a killed activity's ImageView from being garbage collected. This explains why we have to check that both the weak reference and theimageVieware not null (i.e. were not collected) before using them inonPostExecute.This simplified example illustrates the use on an
AsyncTask, and if you try it, you'll see that these few lines of code actually dramatically improved the performance of the ListView which now scrolls smoothly. Read Painless threading for more details on AsyncTasks.However, a ListView-specific behavior reveals a problem with our current implementation. Indeed, for memory efficiency reasons, ListView recycles the views that are displayed when the user scrolls. If one flings the list, a given ImageView object will be used many times. Each time it is displayed the ImageView correctly triggers an image download task, which will eventually change its image. So where is the problem? As with most parallel applications, the key issue is in the ordering. In our case, there's no guarantee that the download tasks will finish in the order in which they were started. The result is that the image finally displayed in the list may come from a previous item, which simply happened to have taken longer to download. This is not an issue if the images you download are bound once and for all to given ImageViews, but let's fix it for the common case where they are used in a list.
Handling concurrency
To solve this issue, we should remember the order of the downloads, so that the last started one is the one that will effectively be displayed. It is indeed sufficient for each ImageView to remember its last download. We will add this extra information in the ImageView using a dedicated Drawable subclass, which will be temporarily bind to the ImageView while the download is in progress. Here is the code of our
DownloadedDrawableclass:static class DownloadedDrawable extends ColorDrawable { private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { super(Color.BLACK); bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); } public BitmapDownloaderTask getBitmapDownloaderTask() { return bitmapDownloaderTaskReference.get(); }}This implementation is backed by a
ColorDrawable, which will result in the ImageView displaying a black background while its download is in progress. One could use a “download in progress” image instead, which would provide feedback to the user. Once again, note the use of a WeakReference to limit object dependencies.Let's change our code to take this new class into account. First, the
downloadmethod will now create an instance of this class and associate it with the imageView:public void download(String url, ImageView imageView) { if (cancelPotentialDownload(url, imageView)) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); imageView.setImageDrawable(downloadedDrawable); task.execute(url, cookie); }}The
cancelPotentialDownloadmethod will stop the possible download in progress on this imageView since a new one is about to start. Note that this is not sufficient to guarantee that the newest download is always displayed, since the task may be finished, waiting in itsonPostExecutemethod, which may still may be executed after the one of this new download.private static boolean cancelPotentialDownload(String url, ImageView imageView) { BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); if (bitmapDownloaderTask != null) { String bitmapUrl = bitmapDownloaderTask.url; if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { bitmapDownloaderTask.cancel(true); } else { // The same URL is already being downloaded. return false; } } return true;}
cancelPotentialDownloaduses thecancelmethod of the AsyncTask class to stop the download in progress. It returnstruemost of the time, so that the download can be started indownload. The only reason we don't want this to happen is when a download is already in progress on the same URL in which case we let it continue. Note that with this implementation, if an ImageView is garbage collected, its associated download is not stopped. ARecyclerListenermight be used for that.This method uses a helper
getBitmapDownloaderTaskfunction, which is pretty straigthforward:private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) { if (imageView != null) { Drawable drawable = imageView.getDrawable(); if (drawable instanceof DownloadedDrawable) { DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable; return downloadedDrawable.getBitmapDownloaderTask(); } } return null;}Finally,
onPostExecutehas to be modified so that it will bind the Bitmap only if this ImageView is still associated with this download process:if (imageViewReference != null) { ImageView imageView = imageViewReference.get(); BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); // Change bitmap only if this process is still associated with it if (this == bitmapDownloaderTask) { imageView.setImageBitmap(bitmap); }}With these modifications, our
ImageDownloaderclass provides the basic services we expect from it. Feel free to use it or the asynchronous pattern it illustrates in your applications to ensure their responsiveness.Demo
The source code of this article is available online on Google Code. You can switch between and compare the three different implementations that are described in this article (no asynchronous task, no bitmap to task association and the final correct version). Note that the cache size has been limited to 10 images to better demonstrate the issues.
Future work
This code was simplified to focus on its parallel aspects and many useful features are missing from our implementation. The
ImageDownloaderclass would first clearly benefit from a cache, especially if it is used in conjuction with a ListView, which will probably display the same image many times as the user scrolls back and forth. This can easily be implemented using a Least Recently Used cache backed by aLinkedHashMapof URL to BitmapSoftReferences. More involved cache mechanism could also rely on a local disk storage of the image. Thumbnails creation and image resizing could also be added if needed.Download errors and time-outs are correctly handled by our implementation, which will return a
nullBitmap in these case. One may want to display an error image instead.Our HTTP request is pretty simple. One may want to add parameters or cookies to the request as required by certain web sites.
The AsyncTask class used in this article is a really convenient and easy way to defer some work from the UI thread. You may want to use the
Handlerclass to have a finer control on what you do, such as controlling the total number of download threads which are running in parallel in this case.
[This post is by Rich “geekyouup” Hyndman, inspired by the fact that life just got that little bit easier — Tim Bray]
Whether you have just started out in Android app development or are a veteran of the craft, it probably won’t be too long before you’ll need to implement horizontally scrolling sets of views. Many existing Android apps already use this UI pattern, such as the new Android Market, Google Docs and Google+. ViewPager standardizes the implementation.
ViewPager was released as part of the Compatibility Package revision 3 and works with Android 1.6 upwards. After following the instructions to obtain the package you can right-click on your Android project in Eclipse, choose ‘Android Tools’ and ‘Add Compatibility Library’, making the new classes available.
ViewPager is a ViewGroup and works in a similar manner to AdapterViews (like ListView and Gallery) so it shouldn’t feel too foreign. Note that if you use ViewPager in an xml layout, be sure to use the full class reference, e.g.
<android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" … />ViewPagers source their views from PagerAdapters which give you have full control over the reuse and recycling of the views. A PagerAdapter implementation called FragmentPagerAdapter is provided to facilitate the use of Fragments in a ViewPager; This is immensely powerful and as simple as implementing getCount() and getItem(). There is a sample called Fragment Pager Support provided in the Support Demos to illustrate this.
public static class MyAdapter extends FragmentPagerAdapter { public MyAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return NUM_ITEMS; } @Override public Fragment getItem(int position) { return ArrayListFragment.newInstance(position); } }FragmentPagerAdapter will detach each fragment as you swipe through the list, but keep them in memory so they can simply be reattached when the user swipes back. If you have a larger number of Fragments, the FragmentStatePagerAdapter is worth considering as it will remove them, with the downside being they need to be rebuilt as the user swipes back to them. So, if you have fewer, more complex fragments the FragmentPagerAdapter makes sense, but consider FragmentStatePagerAdapter for larger sets.
On the more simplistic side I recently wrote a ViewPager/PagerAdapter example that serves up simple TextViews. One thing to note is that if you are implementing your own PagerAdapter it is up to you, the developer, to add and remove your views to and from the ViewGroup. To facilitate this the ViewPager is passed into the PagerAdapter methods instantiateItem() and destroyItem().
@Override public Object instantiateItem(View collection, int position) { View v = layoutInflater.inflate(...); ... ((ViewPager) collection).addView(v,0); return tv; } @Override public void destroyItem(View collection, int position, Object view) { ((ViewPager) collection).removeView((TextView) view); }The source code for ViewPager is also included and available in <android-sdk>/extras/android/compatibility/v4/src. It is worth checking as you can generate the reference documentation from it using Javadoc. In the reference docs / source you’ll find other useful methods, for example setOnPageChangeListener(), which enables your application to track which View is currently visible.
If you are launching an app onto Android Market that uses ViewPager then please ping me on Google+ or Twitter, I’d love to see how widely it is being used and the innovative scenarios in which it appears.