Sometimes a good regime creates a friendly environment for development.
By this I mean that rules forced in an operating system for the programmers should make it obvious how to code and how not to code. Apple for instance set very clear rules on running applications in the background (no applications in the background!), but also gave developers innovative push notifications. Android often follows with those innovations in the design of the Android API, but still leaves a lot of open space for doing things in the wrong way. The once famous quote by Magnus Lycka “It seems to me that Java is designed to make it difficult for programmers to write bad code, while Python is designed to make it easy to write good code.” is turning away from the former language even more, once Java became an interface for programming a wide area of system services and applications in Android.
I’ve met many people puzzled by the way android Activities work, especially when it comes to construction, destruction, and even things as simple as rotation (that involves destruction and re-construction of these objects). The simple answer is: never expect your activities to be there, like “Nobody expects the Spanish inquisition” in Monty Python’s sketches.
* If you’re an advanced Android developer, I’ll save you reading about the Activities, so click through to go to the juicy part of this article
While developers can decide how activities are launched in tasks, their construction and destruction is managed by system and they might be often left in one of the intermediate states, where they are not visible or probably about to shut down (see more about it in the Android Developers documentation).
The example is when we receive the phone call while browsing the application, the phone app comes to the foreground, and depending on the memory conditions, the application might even be shut down in the background (although it doesn’t have to). When we hang up the call, we expect to come back to the application without losing any introduced data. This data is often not in any permanent storage, but still in the UI (UI components like edit boxes, and additionally member variables). If not saved properly in the onSaveInstanceState() and restored in onCreate(savedInstanceState) or onNewIntent(savedInstanceState), these temporary changes might disappear.
As I mentioned previously, rotating the screen has the same effect on the Activities life-cycle, like if it was kicked out because of the lack of memory. It goes down all the way to the onDestroy() and gets recreated again. Developers often prevent it by allowing only vertical orientation of the application or even creating static member fields in the activity. This covers only half of the problem, and it’s more of a hack than the permanent solution. Member variables should be serialized using the Parcelable interface when activity requests that.
There are some objects that can’t be treated that way, i.e. background tasks. It’s a bad practice to re-instantiate the task when the activity got re-created. Just imagine fetching a large image file multiple times. There are two solutions for this problem. One is using the retainNonConfigurationInstance() request from the Activity, which returns the object that shall not be destroyed while activity is about to be recreated (although this approach has been deprecated in favour of the new ActivityFragment architecture). Another solution is to use Service to maintain long-running tasks in the background.
Well, all this information above could be surprising for the Android newbie. But there is another case of writing a quick but problematic one-line code that caught my eye recently.
Android doesn’t help developers handle remote image content easily. It requires quite a lot of code to transparently load images from either URL or the local content provider or the file stored somewhere on the file system. It would be nice yet if it was cached in the memory so we don’t have to reload it from either source each time. And the cache could have a limit depending on the device capabilities. And the CPU load should be optimised to let the more important content appear on the screen first. We’ve seen it all work somewhere, whether it’s a browser app, Gmail or twitter. Images are loaded smoothly while we browse the content.
Koushik Dutta created a great library for caching images from remote sources (whether they’re on the internet or just somewhere behind the content:// provider) and made it very flexible, so one can make it work right away with a very little effort but also use it in a bit more complex way. At the same time, the example provided with the app shows a very improper use of it’s own library. The activity fetches a JSON feed of images from Google, using the phrase provided by the user. Then it passes a list of URLs to the adapter and starts loading them.
//in the http thread // add the results to the adapter runOnUiThread(new Runnable() { @Override public void run() { for (String url: urls) { mAdapter.add(url); } } }); //in the adapter @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView iv; if (convertView == null) convertView = iv = new ImageView(UrlImageViewHelperSample.this); else iv = (ImageView)convertView; // yep, that's it. it handles the downloading and showing an interstitial image automagically. UrlImageViewHelper.setUrlDrawable(iv, getItem(position), R.drawable.loading); return iv; }
Right, this magically (almost) works, because adding each element to the list causes an obscure notifyDataSetChanged() call in the adapter and all list elements get redrawn. Some images might not get drawn in the right place though, but it will be hard to see in such a long list which we will be scrolling up and down, until all images are loaded…
Why is that? Adapters recycle list view entries as long as it’s possible (see convertView in the getView method), so the reference to the imageView will be the same across all the list. If we try to load images in all the entries, we will initialize the downloading in the right place, but when the result arrives ready to draw, only the last element in the list will be updated. Other list entries are only “dead” copies of the convertView (probably drawn to the canvas only once).
What would the solution be? Well there is one, already in the library. The method setUrlDrawable is overloaded with a bunch of different arguments. The one would be:
public static void setUrlDrawable(final ImageView imageView, final String url, final int defaultResource, final UrlImageViewCallback callback);
with the imageView set to null and callback set to the calling activity that would implement the onLoaded method with calling mAdapter.notifyDataSetChanged();
But remember about the activity construction and destruction, huh? What will happen to the orphaned tasks when we recreate the activity? How do we re-attach the observer to the tasks when needed?
I’ve created my own library that fetches images from the internet and content providers, and there I propose how to use it with the activities in the example. It forces the right coding style for refreshing the on-screen content by using the observer/callback pattern instead of holding the references to the views that might get invalidated with other data in the meantime. Please take look at the library and use it. And tell your fellow programmers to try it too. I welcome all comments. There’s surely room for improvement, but it might surprise you nicely with it’s functionality. And I hope the way it is designed helps understanding how it works.
https://github.com/mobiosis/images-android
https://play.google.com/store/apps/details?id=com.mobiosis.imagesexample
I would also prefer the android Lint to help developers design code in the way it is more error-proof in the areas of object instance management. This part that is likely forgotten by developers with non-C experience (hello GarbageCollector). Although completely correct, this kind of code gives me a bit of a headache:
search.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // background the search call! Thread thread = new Thread() { @Override public void run() { try { // clear existing results runOnUiThread(new Runnable() { @Override public void run() { mAdapter.clear(); } });
But when used in a recursive call becomes a really big pain, as mentioned above. It is cool to create instances on the spot, but putting extra effort to keep track of them has actually more value.
Other than that, Java is a really cool language (especially in Android). One thing I learned at the beginning of my experience with Android programming was: when there’s a problem, there must be some solution. This comic illustrates it quite well.
(with the courtesy of AndroidPolice)
PD. Anyway, thanks to Koushik Dutta for creating a really great piece of code. I don’t try minimise it’s value. It is a very useful library, although I got to know about it very recently, so I created something apart.
Also thanks to Oriol Jimenez for his very inspirational presentation at GDG DevFest in Barcelona and DroidconES in Murcia later this year. You can find more good advices there (if you understand Spanish).