Dealing with EMFILE(Too many open files) & OOM(OutOfMemory) Exceptions in Android

Today I’m going to write about some of the issues I faced while developing an Image Gallery with caching techniques, efficient bitmap loading and what tactics that I’ve taken to overcome the issues.

I have developed a custom image gallery with the use of android GridView. Key requirement was to load large amount of images with limited time. In addition the user should be able to smoothly scroll up-down the gallery control.

In order to accomplish this, I have used caching techniques like LruCache, scale down the images before loading (low memory consumption) and loading the images asynchronously (with the use of AsyncTask).

I experienced the above mentioned EMFILE(Too many open files) exception while I was testing this. At this time there were 50-100 images were loaded into the Image Gallery and I was scrolling up and down for around 10-20 times.

EMFILE(Too many open files)

This was happening due a limitation in Android(We can say this is an inherited behavior of Linux). The reason behind this is Android limits the max open files per process to 1024. So this can be experienced in a scenario like Image Gallery with large amount of files, unless we carefully handle the file processing techniques.

But finally got a clue.

It was the process of scaling down the images. It had few FileInputStream objects which were not closed after consuming.

protected static Bitmap decodeFile(File imageFile, int requiredHeight, int requiredWidth) throws Exception {

	// Decode image size
	BitmapFactory.Options oSize = new BitmapFactory.Options();

	oSize.inJustDecodeBounds = true;
	oSize.inDither = false;
	oSize.inPurgeable = true;
	oSize.inInputShareable = true;

	//FileInputStream is used, but it's not closing after consuming...
	BitmapFactory.decodeStream(new FileInputStream(imageFile), null, oSize);

	// Find the correct scale value. It should be the power of 2.
	int width_tmp = oSize.outWidth, height_tmp = oSize.outHeight;
	int scale = 1;
	while (true) {
		if (width_tmp <= requiredWidth || height_tmp <= requiredHeight)
			break;
		width_tmp /= scale;
		height_tmp /= scale;
		scale *= 2;
	}

	// Decode with inSampleSize
	BitmapFactory.Options oPreview = new BitmapFactory.Options();
	oPreview.inSampleSize = scale;
	oPreview.inDither = false;
	oPreview.inPurgeable = true;
	oPreview.inJustDecodeBounds = false;
	oPreview.inInputShareable = true;

	//FileInputStream is used, but it's not closing after consuming...
	return BitmapFactory.decodeStream(new FileInputStream(imageFile), null, oPreview);

}

Following is the modified version of the code block, and I was able to get rid of EMFILE(Too many open files) exception.


protected static Bitmap decodeFile(File imageFile, int requiredHeight, int requiredWidth) throws Exception {

	FileInputStream fileInputStreamIn = null;
	FileInputStream fileInputStreamOut = null;
	
	try{
		// Decode image size
		BitmapFactory.Options oSize = new BitmapFactory.Options();
		
		oSize.inJustDecodeBounds = true;
		oSize.inDither = false;
		oSize.inPurgeable = true;
		oSize.inInputShareable = true;
		
		fileInputStreamIn = new FileInputStream(imageFile);
		BitmapFactory.decodeStream(fileInputStreamIn, null, oSize);

		// Find the correct scale value. It should be the power of 2.
		int width_tmp = oSize.outWidth, height_tmp = oSize.outHeight;
		int scale = 1;
		while (true) {
			if (width_tmp <= requiredWidth || height_tmp <= requiredHeight)
				break;
			width_tmp /= scale;
			height_tmp /= scale;
			scale *= 2;
		}

		// Decode with inSampleSize
		BitmapFactory.Options oPreview = new BitmapFactory.Options();
		oPreview.inSampleSize = scale;
		oPreview.inDither = false;
		oPreview.inPurgeable = true;
		oPreview.inJustDecodeBounds = false;
		oPreview.inInputShareable = true;
		
		fileInputStreamOut = new FileInputStream(imageFile);
		return BitmapFactory.decodeStream(fileInputStreamOut, null, oPreview);
	}
	finally{
		//Got rid of EMFILE Exception(Too many file open)
		if(fileInputStreamIn != null)
			fileInputStreamIn.close();
		if(fileInputStreamOut != null)
			fileInputStreamOut.close();
	}

}

OOM(OutOfMemory)

The application got crashed, soon after I left the screen which had the Image Gallery. This was due to OOM Exception. This issue was also bit tricky, since there was no straight forward way of identifying the reason behind this. The Image Gallery (Custom GridView) Adapter was fully enhanced to reuse the views (using of ViewHolder pattern which Android has suggested), so I thought this wasn’t causing the error. Then my eyes turned into the implementation of LruCache. There was a slight mistake in LruCache, because when the time of initializing it I have used a fixed size for it.

So, DON’T DO THIS,


mMemoryCache = new LruCache<String, Bitmap>(30)

because what Android has suggested is, set the size of LruCache dynamically depending of the available memory.


final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

final int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
	@Override
	protected int sizeOf(String key, Bitmap bitmap) {
		return (bitmap.getRowBytes() * bitmap.getHeight()) / 1024;
	}
};

With the help of above modification, I was able to get rid of the OOM Exception.

Happy Coding…. 🙂

Creating a workable Android 5.0 (Lollipop) Emulator

At the beginning I was struggling while setup and running a Android Lollipop emulator. So I thought putting all the steps into one place and sharing it in a simpler manner.

This was fully tested in Windows 7 (64 bit) environment with Eclipse JUNO Version: 4.2.1.

1. Please make sure that you have the latest Android SDK Tools and Android SDK Platform-tools. If not please update them to the latest version.

2

2. Install necessary components of Android 5.0(API21). For the moment, I didn’t install the components related to Android TV.

4

3. Make sure that you have installed Android SDK Build-tools -> Rev. 21.

3

3. Make sure that you have the latest component of Android Support Library & Google Play services.

5

4. You must install Intel x86 Emulator Accelerator(HAXM installer). Don’t use the Android SDK Manager for installing that.

6

Because at the moment the SDK Manager gives you HAMX installer version 1.1.0. But to build up Android Lollipop emulator, it requires the latest version of HAMX which is 1.1.1. This can be found in the following link Intel x86 Emulator Accelerator(HAMX installer)

5. Next go and build the Android Lollipop emulator.

6. I have selected Nexus 5 as the device.

11

7. When you are creating the Android Lollipop emulator(AVD) make sure to select Android 5.0-API Level 21 as the Target.

7

If you select Google APIs – API Level 21, you might get a warning even if you have installed Google APIs Intel Atom(x86) system image.

Warning

8. For CPU/ABI, I have chosen Google APIs Intel Atom(x86_64).

8

9. Make sure that you have selected Use Host GPU option rather than Snapshot in Emulation Options. Otherwise when the emulator starts you will see nothing but a black blank screen only.

9

Finally you are ready to go. Start your newly created Android Lollipop emulator(AVD) from the AVD list.

12

Generic Parcelable Implementation

Today I’m going to post you about a generic implementation of Parcelable interface in Android.

Implementation


import android.os.Parcel;
import android.os.Parcelable;

public class GenericParcelable<T> implements Parcelable {

	private T mValue;
	private static ClassLoader mClassLoader;

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel parcelOut, int flags) {
        parcelOut.writeValue(mValue);
    }

    @SuppressWarnings("rawtypes")
	public static final Parcelable.Creator<GenericParcelable> CREATOR = new Parcelable.Creator<GenericParcelable>() {

        public GenericParcelable createFromParcel(Parcel in) {
            return new GenericParcelable(in);
        }

        public GenericParcelable[] newArray(int size) {
            return new GenericParcelable[size];
        }
    };

    public GenericParcelable(T value){
    	this.mValue = value;
    	if(this.mValue != null)
    		GenericParcelable.mClassLoader = value.getClass().getClassLoader();
    } 

	@SuppressWarnings("unchecked")
	private GenericParcelable(Parcel parcelIn) {
    	try{
            //reading the passed value
    		mValue = (T)parcelIn.readValue(GenericParcelable.mClassLoader);
    	}
    	catch(Exception e){
    		e.printStackTrace();
    	}
    }

    //generic method of obtaining the parcel value
	public T getValue(){
		return (T) mValue;
    }

}

Usage

This is the way of adding a value to Parcelable object in an Activity class.


Intent intent = new Intent(this, NextActivity.class);
//Create the Parcelable object with an int value
intent.putExtra("myKey", new GenericParcelable<Integer>(15));
startActivity(intent);

Obtaining the value in a different Activity.

int passedValue;
GenericParcelable<Integer> genericParcelable = getIntent().getParcelableExtra("myKey");
if (genericParcelable != null){
   passedValue = genericParcelable .getValue();
}

Happy coding 🙂

Unexpected behavior of text color transparency in Android 4.0.4

Came across an unexpected behavior when setting the text color of a Check box while developing an android application. Actually the text color which is transparent color was set in the XML layout like below.

<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/text_state"
    android:textColor="#00000000">
</CheckBox>

The code above worked as expected in Android 2.3.4, 4.1.2. But the transparency trick didn’t work in Android 4.0.4. But when I refer this color via the resource file, it worked in all versions including 4.0.4

Ex:

<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/text_state"
    android:textColor="@color/my_transparent">
</CheckBox>

Walkthrough for Installing the Team Foundation Server Plug-in for Eclipse

Even though I have experienced in using Tortoise-SVN, I’m a newbie for Team Foundation Server which provides by Microsoft. At the moment I am involved in an Android Project and for that I have to use TFS as the Version Controller and Eclipse as my IDE.

Here is one of the best walkthrough which describes how to install & configure TFS Plugin-in for Eclipse.

Note: I’m using the latest Eclipse IDE(Build: v22.3.0-887826) which provided via Windows ADT Bundle.

Visual Studio 2012 Premium + Team Foundation Server Error When Comparing Files + “Cannot load the configured tool..”

I’m a newbie to Team Foundation Server source control. I came across an issue few days back while I was using it. When I tried to compare a file, I got the below shown error.vfs

 

 

 

 

In my Office-PC, I had Windows 7 Enterprise 64x Edition(Service Pack 1), and Visual Studio 2012 Premium Edition(not with the latest update). After spending sometime on the net, I found out that for comparing feature Team Foundation Server uses a file called “vsDiffMerge.exe“.If it was there, the location for the path would be

  • 64bit – “C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE
  • 32bit – “C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE

But later I found out that, “vsDiffMerge.exe” was not in the specified location.

The solution was to install the latest Update for VS2012(Update 3/4). http://www.microsoft.com/en-us/download/details.aspx?id=39305

After that All Is Well. File comparing feature will work smoothly.

 

Developing a Simple Google Map Application in Android [debug/release modes] (Android 4.0, Google Play Service, Google Maps Android API v2)

Today I’m going to post about an android application which uses Google Maps. I’m going to use Google Maps Android API v2(since Google Maps Android API v1 is deprecated) and it is not easy to use as Google Maps Android API v1. The steps which you have to follow is mentioned below.

Remember, please don’t waste time try running this application with an Emulator. I have seen many people tried run these types of application with an Emulator, but most of the time it has failed.

My Development Environment

Samsung S2(GT-I9100) with Android version 4.0.3(which was upgraded from Android 2.0+). Also this is quite important, I had the latest Google Play Service installed in it.

If you have this kind of environment I guaranteed that the application will run smoothly.

Because of Google Maps Android API v2, is strongly connected with Google Play Service library, you need to download it first. You can easily do it using the Android SDK Manager.

Play

Once it is downloaded you can find a folder called google-play-services_lib. It will be available in your Android-SDK -> Extras -> Google -> google_play_services -> libproject folder.
Ex: C:\Program Files (x86)\Android\android-sdk\extras\google\google_play_services\libproject\google-play-services_lib

Import it as an android project to your Eclipse environment/your workspace.

project-import

Once the project is imported, we can start implementing our Android Map Application.

First you have to create an Android Project, in my case I set the target Platform into Android 4.0 & API Level 14. My project name was Rakimap and the package name was com.rakimap. The Activity name was MainActivity, which is added by default.

Then what you have to do is you have to reference the google-play-services_lib project which you have imported earlier.

import-play

Then it comes to the most easiest but trickiest part of the project, In order to you the Google Map API Services you have to obtain a key. Whether you are in the debug environment or release environment you have to use a key in order to use the facilities of this API. So here’s how you do it.

Debug Environment

You need to use the debug.keystore file, if you are using Eclipse IDE you will be able to find it under Users -> <current-user-name> -> .android folder. Ex: C:\Users\rawilk\.android

The open the command prompt and navigate to you jdk-version\bin folder (Ex: C:\Program Files (x86)\Java\jdk1.5.0\bin), where you can find the keytool.exe. In there you have to execute this command.

keytool.exe -list -v -keystore “<file path>\debug.keystore” -alias androiddebugkey -storepass android -keypass android

debug key

Once it is executed it will response back with set of lines.

debug key-sha

There you can find two types of certificate fingerprints and you have to copy the SHA1 value for later usage.

Then you should visit this link Google Api Console. As I prefer the new Google Cloud API Console, you will be seen a page like this once you logged-in. You have to go to APIs section which is under APIs & auth. In there if the Google Maps Android API v2 status is OFF, turn it ON.

google-api-console

Once it’s done move to the Credentials section. In there click the Create new Key button.

google-api-console-new-key

Once you click it a popup dialog will be opened. There you press the button named as Android key.

google-api-console-new-key-android

Once you click it you will be seen another pop-up screen which asks the SHA1 value which you have copied earlier. Once you put it, then enter a semi-colon and then enter the name of the package as it is in the project. (This is the package which contains the Activity which will be used for displaying the Google Map later in this). In my case it was com.rakimap.

google-api-console-new-key-android-sha

Once you click the Create button you will be able to get the API key. Copy it and keep it somewhere because we are going to use it later in the project that you have created.

Finally here we go with the blank activity which you created earlier. Let’s make it really simple. Here’s how the MainActivity class would be.

package com.rakimap;

import android.app.Activity;
import android.os.Bundle;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MainActivity extends Activity {

	static final LatLng COLOMBO = new LatLng(6.927, 79.861);
	private GoogleMap map;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map))
				.getMap();

		Marker markerColombo = map.addMarker(new MarkerOptions().position(
				COLOMBO).title("Hamburg"));

		// Move the camera to Colombo area with the zoom level of 20.
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(COLOMBO, 20));

		// Animating the camera while zooming.
		map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);
	}

}

Make the layout file like this. In my case it was the activity_main.xml.


<RelativeLayout 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"
    tools:context=".MainActivity" >

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment" />

</RelativeLayout>

Here’s how the AndroidManifest.xml is going to be.

You have to put the obtained Google API Key inside a meta-data tag block, as well as set some additional user permissions as below mentioned.  Also remember to use the correct package name where necessary.
Ex: com.rakimap.permission.MAPS_RECEIVE

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.rakimap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="14" />

    <permission
        android:name="com.rakimap.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-permission android:name="com.rakimap.permission.MAPS_RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.rakimap.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyAb8-sm4w3X9SXAHFCDhFdDZkayPfLjgiw" />
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>

Once this is done, try building the project to rectify any compile time errors. If there are no errors, plug-in your android device(I hope you have the per-requisites knowledge of how to handle an android device in development environment) and try deploying it.

You will be seen this if All Is Well.

Screenshot_2014-01-10-10-53-55

Release Environment

When it comes to release the application as an APK, obtaining of the Google API Key will same, but you need to create a separate key-store for this.(you won’t automatically get a key-store like debug.keystore).

Open command prompt and navigate to the location of keytool.exe. Once you there you have to execute the below command in order to create the private key which you will be used in exporting the android application.

keytool.exe -genkey -v -keystore “<path of the release key to be stored>\<name of the file>” -alias <release key alias name> -keyalg RSA -keysize 2048 -validity 10000

Ex: keytool.exe -genkey -v -keystore “C:\RakiMap\raki-release.keystore” -alias raki-release-key -keyalg    RSA -keysize 2048 -validity 10000

release-key1

Once you execute this, you will be asked a series of questions which you need to be answered. Answering for some of the questions are not needed. Refer the below shown image to get an idea.

release-key2

If the key generation is successful(please locate where it resides), you can follow the same steps to obtain a new Google API Key for release mode. Before that you need to get the SHA1 value for the newly generated key. To do that execute the below command.

keytool.exe -list -v -keystore “<path of the release key to be stored>\<name of the file>”

Ex: keytool.exe -list -v -keystore “C:\RakiMap\raki-release.keystore”

release-key3

So once you obtain the new Google API Key for release mode, you have to modify the AndroidManifest.xml file.

Then you can rebuild the program and you are ready to export the application for releasing. I hope you know the initial steps of exporting the android application when it comes to the Keystore selection dialog, you have to set the newly generated keystore file and use the same password which you used when building the key.

release1

Once you press next you will be seen a dialog like this. There you have to select the alias name which you have given when you building the key. Use the same password as well.

release2

After that it is pretty straightforward, you have to set a path to generate the APK file. Once the APK file is generated try installing it in the device. If All Is Well you will be seen this again.

Screenshot_2014-01-10-10-53-55