Wednesday, 7 August 2013

Memory management for Android apps

Memory management for Android Apps
Android apps have more memory available than ever before, but are you sure you're using it wisely? Even though Android has got a garbage collector, do I have to worry about memory management?
In this post I will discuss about:
  • how heap size affects applications
  • how GC (garbage collector) works and memory leak
  • how to handle bitmap efficiently

Memory Heap Size

Android is a full multitasking system so it’s possible tu run multiple programs at the same time and obviously each one can’t use all of your device memory.
For this reason there is a hard limit on your Android application’s heap size: if your application needs to allocate more memory and you have gone up to that heap size already, you are basically going to get a “out of memory error”.
Heap size limit is device dependent and it has changed a lot over the years, the first Android phone (the G1) has 16MB heap size limit, now we have tablets like Motorola Xoom that have 48MB heap size limit.
The basic reason is that new tablets and new phones have an high monitor resolution so they need more memory in order to draw more pixels.
However this limit is also too tight for a new kind of application that you can image for a tablet. For example, think about a photo editor that is really memory intensive, probably 48MB of heap space isn’t enough for that.
For that kind of application Honeycomb introduced an option in the Android Manifest: “android:largeHeap=true” that give you a big heap space limit.”
This doesn’t mean that if you get an “out of memory error” you can fix it simply setting this option!
You have to use this option only if you are going to build an application that is really memory intensive and you really need it.
When you develop an application you have to think that your application will run on a very large set of different devices with different capability and different heap size limit, so you need to worry about that and a simple thing you can do it’s provide your bitmap resources in all the different resolution ldpi, mdpi, hdpi, xhdpi.

Garbage Collector

The goal of the garbage collector is to keep the memory clean from unreferenced object.
Each object in memory must be connected directly or indirectly to the GC Roots, otherwise they can be removed from garbage collector.
All the objects in memory are represented by a tree and the garbage collector start traversing the tree from the GC roots to find unreferenced objects that need to be cleaned.
In the follow picture, red dots aren’t connected to the the GC roots so they can’t be cleaned.
memory-management-for-android-apps-en.png
Garbage collector make a good work but doesn’t prevent memory leaks. A leak is a reference to an unused object that prevent the GC to collect it.
A common mistake that usually generates a leak it’s a long reference to an Activity, a View or a Drawable.
In the follow example I will show you a circumstance that crates a leak.
memory-management-for-android-apps-en-1.png
The code used to generate this situation is very simple:
public class MainActivity extends Activity {
 
 private static Leaky mLeak;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  if (mLeak == null) {
   mLeak = new Leak();
  }
 }
 
 class Leaky {
  public void hello() {
   System.out.println("hello");
  }
 }
}
The problem in the side code is that the MainActivity has a static field that refers to an object of a non-static inner class. Non-static Inner class keeps a reference to enclosing object and
static fields are related to the class not to the instance of an object. So the mLeak field will live until MainActivity.class is freed and keep a reference to the MainActivity that instantiate the mLeak field.

Handle bitmap efficiently

If you're not careful, bitmaps can quickly consume your available memory budget leading to an application crash due to the dreaded exception: “java.lang.OutofMemoryError: bitmap size exceeds VM budget”.
Images come in all shapes and sizes. In many cases they are larger than required for a typical application user interface (UI).
Given that you are working with limited memory, the best thing that you can do is to load a lower resolution version into the memory. The lower resolution version should match the size of the UI component that displays it. An image with an higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.
The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(),decodeResource(), etc.) for creating a Bitmap from various sources.
Each type of decode method has additional signatures that let you specify the decoding options via BitmapFactory.Options class.
Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data before the construction (and memory allocation) of the bitmap.
Now that the image dimensions are known, they can be used to decide if we should load the full image or a subsampled version, into the memory. To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to the scale factor in your BitmapFactory.Options object.
The code below shows how to load an Image of arbitrarily large size to a bitmap of the reqWidth and reqHeight.
The calcualteInSampleSize just determinates the scale factor from the original size and the requested size.
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {
    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}
References:
http://android-developers.blogspot.it/2011/03/memory-analysis-for-android.html
http://android-developers.blogspot.it/2009/01/avoiding-memory-leaks.html
http://developer.android.com/training/displaying-bitmaps/index.html

Monday, 5 August 2013

Working With Images In Android

Using BitmapDrawable To Create Bitmaps

BitmapDrawables are simply Drawable objects that wrap a bitmap and can be created from file path, input stream, XML inflation from a layout, and bitmaps.

Building Bitmap Objects

File

Use the adb tool with push option to copy test2.png onto the sdcard.

bash-3.1$ /usr/local/android-sdk-linux/tools/adb push test2.png /sdcard/


This is the easiest way to load bitmaps from the sdcard. Simply pass the path to the image to BitmapFactory.decodeFile() and let the Android SDK do the rest.

package higherpass.TestImages;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;

public class TestImages extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView image = (ImageView) findViewById(R.id.test_image);
        Bitmap bMap = BitmapFactory.decodeFile("/sdcard/test2.png");
        image.setImageBitmap(bMap);
    }
}


All this code does is load the image test2.png that we previously copied to the sdcard. The BitmapFactory creates a bitmap object with this image and we use the ImageView.setImageBitmap() method to update the ImageView component.

Input stream

Use BitmapFactory.decodeStream() to convert a BufferedInputStream into a bitmap object.

package higherpass.TestImages;

import java.io.BufferedInputStream;
import java.io.FileInputStream;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;

public class TestImages extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView image = (ImageView) findViewById(R.id.test_image);
        FileInputStream in;
        BufferedInputStream buf;
        try {
       	    in = new FileInputStream("/sdcard/test2.png");
            buf = new BufferedInputStream(in);
            Bitmap bMap = BitmapFactory.decodeStream(buf);
            image.setImageBitmap(bMap);
            if (in != null) {
         	in.close();
            }
            if (buf != null) {
         	buf.close();
            }
        } catch (Exception e) {
            Log.e("Error reading file", e.toString());
        }
    }
}


This code uses the basic Java FileInputStream and BufferedInputStream to create the input stream for BitmapFactory.decodeStream(). The file access code should be surrounded by a try/catch block to catch any exceptions thrown by FileInputStream or BufferedInputStream. Also when you're finished with the stream handles they should be closed.

XML inflation

Bitmaps can be extracted from layouts and views with inflation. Use BitmapFactory.decodeResource(res, id) to get a bitmap from an Android resource.

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView image = (ImageView) findViewById(R.id.test_image);
        Bitmap bMap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
        image.setImageBitmap(bMap);
    }


First create an ImageView instance containing the ImageView from the layout. Then create a bitmap from the application icon (R.drawable.icon) with BitmapFactory.decodeResource(). Finally set the new bitmap to be the image displayed in the ImageView component of the layout.

Bitmaps

The BitmapFactory.decodeByteArray() method of creating bitmaps creates a bitmap from an array of bytes. This is useful when a bitmap has been loaded for another purpose or has been created by the application or other external source.

package higherpass.TestImages; 

import java.io.BufferedInputStream;
import java.io.FileInputStream;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;

public class TestImages extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView image = (ImageView) findViewById(R.id.test_image);
        FileInputStream in;
        BufferedInputStream buf;
        try {
       	    in = new FileInputStream("/sdcard/test2.png");
            buf = new BufferedInputStream(in);
            byte[] bMapArray= new byte[buf.available()];
            buf.read(bMapArray);
            Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
            image.setImageBitmap(bMap);
            if (in != null) {
         	in.close();
            }
            if (buf != null) {
         	buf.close();
            }
        } catch (Exception e) {
            Log.e("Error reading file", e.toString());
        }
    }
}


As with the decodeStream() example we open the file in an input stream. This time though we go the extra mile and manually read the file into a byte array. This isn't the best way to do this if you haven't noticed, but it's a simple way to show the functionality. Use the BitmapFactory.decodeByteArray() method to create the bitmap. This function expects 3 parameters, the byte array, the array offset to start from, and the array offset to stop at. 

Sunday, 4 August 2013

Android XML Parsing Tutorial

The XML Structure
In this tutorial i’ll will be parsing the following XML file. You can get this xml file by accessing http://api.androidhive.info/pizza/?format=xml
<?xml version="1.0" encoding="UTF-8"?>
<menu>
    <item>
        <id>1</id>
        <name>Margherita</name>
        <cost>155</cost>
        <description>Single cheese topping</description>
    </item>
    <item>
        <id>2</id>
        <name>Double Cheese Margherita</name>
        <cost>225</cost>
        <description>Loaded with Extra Cheese</description>
    </item>
    <item>
        <id>3</id>
        <name>Fresh Veggie</name>
        <cost>110</cost>
        <description>Oninon and Crisp capsicum</description>
    </item>
    <item>
        <id>4</id>
        <name>Peppy Paneer</name>
        <cost>155</cost>
        <description>Paneer, Crisp capsicum and Red pepper</description>
    </item>
    <item>
        <id>5</id>
        <name>Mexican Green Wave</name>
        <cost>445</cost>
        <description>Onion, Crip capsicum, Tomato with mexican herb</description>
    </item>
</menu>
Writing XML Parser Class
In your project create a class file and name it as XMLParser.java. The parser class mainly deals the following operations.
⇒ Getting XML content by making HTTP request
⇒ Parsing XML content and getting DOM element of xml.
⇒ Get each xml child element value by passing element node name.
Getting XML content by making HTTP Request
This function will get XML by making an HTTP Request.
public String getXmlFromUrl(String url) {
		String xml = null;

		try {
			// defaultHttpClient
			DefaultHttpClient httpClient = new DefaultHttpClient();
			HttpPost httpPost = new HttpPost(url);

			HttpResponse httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			xml = EntityUtils.toString(httpEntity);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// return XML
		return xml;
	}
Parsing XML content and getting DOM element
After getting XML content we need to get the DOM element of the XML file. Below function will parse the XML content and will give you DOM element.
public Document getDomElement(String xml){
		Document doc = null;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {

			DocumentBuilder db = dbf.newDocumentBuilder();

			InputSource is = new InputSource();
		        is.setCharacterStream(new StringReader(xml));
		        doc = db.parse(is); 

			} catch (ParserConfigurationException e) {
				Log.e("Error: ", e.getMessage());
				return null;
			} catch (SAXException e) {
				Log.e("Error: ", e.getMessage());
	            return null;
			} catch (IOException e) {
				Log.e("Error: ", e.getMessage());
				return null;
			}
                // return DOM
	        return doc;
	}
Get each xml child element value by passing element node name
public String getValue(Element item, String str) {
	NodeList n = item.getElementsByTagName(str);
	return this.getElementValue(n.item(0));
}

public final String getElementValue( Node elem ) {
	     Node child;
	     if( elem != null){
	         if (elem.hasChildNodes()){
	             for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
	                 if( child.getNodeType() == Node.TEXT_NODE  ){
	                     return child.getNodeValue();
	                 }
	             }
	         }
	     }
	     return "";
  }	
Usage
Following is the code snippet for handling xml operations. I am using xml parser class so far we built and looping through each xml element and getting the child data.
// All static variables
static final String URL = "http://api.androidhive.info/pizza/?format=xml";
// XML node keys
static final String KEY_ITEM = "item"; // parent node
static final String KEY_NAME = "name";
static final String KEY_COST = "cost";
static final String KEY_DESC = "description";

XMLParser parser = new XMLParser();
String xml = parser.getXmlFromUrl(URL); // getting XML
Document doc = parser.getDomElement(xml); // getting DOM element

NodeList nl = doc.getElementsByTagName(KEY_ITEM);

// looping through all item nodes <item>
for (int i = 0; i < nl.getLength(); i++) {
	String name = parser.getValue(e, KEY_NAME); // name child value
	String cost = parser.getValue(e, KEY_COST); // cost child value
	String description = parser.getValue(e, KEY_DESC); // description child value
}
Parsing XML data and updating into ListView
In my previous tutorial Android ListView Tutorial i explained how to create listview and updating with list data. Below i am implementing same listview but the list data i am updating is from parsed xml. This ListView has multiple sub text like name, cost and description.

public class AndroidXMLParsingActivity extends ListActivity {

	// All static variables
	static final String URL = "http://api.androidhive.info/pizza/?format=xml";
	// XML node keys
	static final String KEY_ITEM = "item"; // parent node
	static final String KEY_ID = "id";
	static final String KEY_NAME = "name";
	static final String KEY_COST = "cost";
	static final String KEY_DESC = "description";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		ArrayList<HashMap<String, String>> menuItems = new ArrayList<HashMap<String, String>>();

		XMLParser parser = new XMLParser();
		String xml = parser.getXmlFromUrl(URL); // getting XML
		Document doc = parser.getDomElement(xml); // getting DOM element

		NodeList nl = doc.getElementsByTagName(KEY_ITEM);
		// looping through all item nodes <item>
		for (int i = 0; i < nl.getLength(); i++) {
			// creating new HashMap
			HashMap<String, String> map = new HashMap<String, String>();
			Element e = (Element) nl.item(i);
			// adding each child node to HashMap key => value
			map.put(KEY_ID, parser.getValue(e, KEY_ID));
			map.put(KEY_NAME, parser.getValue(e, KEY_NAME));
			map.put(KEY_COST, "Rs." + parser.getValue(e, KEY_COST));
			map.put(KEY_DESC, parser.getValue(e, KEY_DESC));

			// adding HashList to ArrayList
			menuItems.add(map);
		}

		// Adding menuItems to ListView
		ListAdapter adapter = new SimpleAdapter(this, menuItems,
				R.layout.list_item,
				new String[] { KEY_NAME, KEY_DESC, KEY_COST }, new int[] {
						R.id.name, R.id.desciption, R.id.cost });

		setListAdapter(adapter);

		// selecting single ListView item
		ListView lv = getListView();
                // listening to single listitem click
		lv.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// getting values from selected ListItem
				String name = ((TextView) view.findViewById(R.id.name)).getText().toString();
				String cost = ((TextView) view.findViewById(R.id.cost)).getText().toString();
				String description = ((TextView) view.findViewById(R.id.desciption)).getText().toString();

				// Starting new intent
				Intent in = new Intent(getApplicationContext(), SingleMenuItemActivity.class);
				in.putExtra(KEY_NAME, name);
				in.putExtra(KEY_COST, cost);
				in.putExtra(KEY_DESC, description);
				startActivity(in);

			}
		});
	}
}