Instantiating a large number of views in Android

A complex layout hierarchy can be an expensive and lengthy operation to instantiate in Android, usually causing the app to pause and look like it’s crashed. Most of the time it’s advisable to use a mechanism that recycles elements, like a ListView, but occasionally you may find yourself requiring a custom layout where recycling won’t work. You probably won’t notice a slowdown until you exceed 100+ elements in a view, and it would be sensible to think about whether there isn’t a better solution, but if there isn’t then the idea below may help you out.

Inflating on a separate thread

In Android you can only manipulate the display hierarchy from the main activity thread (the same goes for most gui frameworks). If you try to manipulate it from another thread it will throw an exception. Lengthy operations on this main thread will block any visual update routine, giving the impression that the app has hung. Adding a view to Android is an example of something that requires the main thread. However adding a view is usually a two step process: 1) inflating the view from the resource, 2) adding the view to a parent in the display hierarchy. The second must occur on the main thread, otherwise it’ll throw an error, but the first step, inflation, can be done on another thread. This effectively halves the time the main thread spends blocked and allows you show to a progress indicator of some description. To do this break the inflation part out into a new AsyncTask, like so:

private ArrayList<View> inflatedViews;
private LayoutInflater inflater;
private RelativeLayout parentView;

private class InflateViews extends AsyncTask
{
	@Override
	protected void onPreExecute()
	{
		// set up anything before the thread starts, like a progress indicator
	}

	protected Boolean doInBackground()
	{
		for (int i=0; i < 100; i++)
		{
			// false at the end tells it not to add the view to the hierarchy
			View myView = (View) inflater.inflate(R.layout.my_view, parentView,
				false);
			inflatedViews.add(myView);
		}
		return true;
	}

	protected void onProgressUpdate(Integer... progress)
	{
		// update your progress indicator here
	}

	protected void onPostExecute(Boolean result)
	{
		for (View myView : inflatedViews)
		{
			parentView.addView(myView);
		}
		// dismiss your progress indicator here
	}
}

private void runInflateTask()
{
	inflater = LayoutInflater.from(getContext())
	parentView = (RelativeLayout) findViewById(R.id.MyParentView);
	new InflateViews().execute();
}

AsyncTask is a simply way to spawn a background thread. The view inflation occurs in doInBackground, and because it doesn’t need to manipulate the display hierarchy it won’t throw an exception. The onPostExecute method runs back on the main thread where it’s safe to add the inflated views. This doesn’t entirely eliminate the pause that occurs, but it significantly reduces it, and opens up the possibility of showing the user visual feedback while the background task executes.

Comments are closed.