User Tools

Site Tools


android-labs-s14:class-08

Class 08

In this class, we'll be learning about:

  • Menus
  • Async Tasks
  • Progress Dialogs
  • Permissions

Menus look something like this in the smartphones with recent Android versions:

Let's open the Project (PlayMusic), which we built in Class-07. We'll add a menu option to this app in a moment.

Step 01: Add items to the Menu

When you'd have created this Project, you'd have noticed a method called onCreateOptionsMenu() created for you. This method, by default, inflates Menu items using an xml file (main.xml) in res/menu folder. If you open main.xml, you should see a menu item (which corresponds to the 'settings' option in the last figure) something like below:

main.xml
	<menu xmlns:android="http://schemas.android.com/apk/res/android" >
 
	    <item
		android:id="@+id/action_settings"
		android:orderInCategory="100"
		android:showAsAction="never"
		android:title="@string/action_settings"/>
 
	</menu>

So, this is one way to inflate the Menu options statically.

  • Instead of 'settings' option, let's have a menu option to 'quit' the app.
    • So, first change the value of android:id to “@+id/action_quit”.
    • Then, change the value of android:title to “Quit”.

Step 02: Implement Menu Option's click callback

  • Whenever a user presses on any Menu options, system calls onOptionsItemSelected() method as its callback. On the press of Quit menu option, we would like to unbind and stop the service and quit the app. So, we can have following implementation for onOptionsItemSelected() method:
MainActivity::onOptionsItemSelected()
	public boolean onOptionsItemSelected(MenuItem item) 
	{
	    // Handle option selection
	    switch (item.getItemId()) 
	    {
	        case R.id.action_quit:
 
			// Unbinding from the service and stopping it
			doUnbindService();
			stopService(new Intent(MainActivity.this, PlayMusicService.class));
 
			// Destroying activity and exiting the app
			finish();
 
	            	return true;
 
	        default:
	    }
 
	    return false;
	}

Splash Screen

Just as an ehancement, you can even add a splash screen in your app with just a one line change in styles.xml, residing in res/values folder.

If you have an image (I named it splash.png) in res/drawable folder, you can then add following code snippet as a child of <style> xml tag with name=AppTheme.

	<item name="android:windowBackground">@drawable/splash</item>

PlayMusicService Source Code

Available on Github.


Demo

A quick look into what we're going to build next.

Android UI (Main) Thread

Android modifies the user interface and handles input events from one single UI thread. This thread is also called the main thread. So, basically, Android collects all events in a queue. If the programmer does not use any concurrency constructs, all code of an Android application runs in the main thread and every statement is executed after each other. If you perform a long lasting operation, for example downloading data from the Internet, the application UI blocks until the corresponding operation has finished. To provide a good user experience, all potentially slow running operations in an Android application should run asynchronously, e.g. via some way of concurrency constructs of the Java language (Thread, java.util.concurrent package) or the Android framework (e.g., AsyncTask, android.os.Handler class).

  • Android enforces a worst case reaction time of applications. If an activity does not react within 5 seconds to user input, the Android system displays an Application not responding (ANR) dialog (as shown in the figure below). From this dialog the user can choose to stop the application.

AsyncTask

The AsyncTask class encapsulates the creation of a background process and the synchronization with the main thread. It also supports reporting progress of the running tasks. It enables you to perform background tasks in a separate thread and then return the result in a UI thread. That way, you can perform background operations without needing to handle complex threading issues.

AsyncTask is designed to be a helper class around ''Thread'' and ''Handler'' and does not constitute a generic threading framework. 
It should ideally be used for short operations (a few seconds at the most.)

To use AsyncTask you must subclass it. AsyncTask uses generics and varargs. The parameters are the following- AsyncTask <TypeOfVarArgParams , ProgressValue , ResultValue>. An AsyncTask is started via the execute() method.

AsyncTaskExample
	protected class DownloadDataTask extends AsyncTask<String, Void, String>
	{
		@Override
		protected String doInBackground(String... params)
		{
			/* Download your data here. */
 
			return null;
		}
	}
 
	public void someCallingMethod()
	{
		DownloadTask downloadTask = new DownloadTask(this);
		downloadTask.execute((String[]) null);
	}

App: AsyncDownload

Cool, so now let's build an application which downloads data asynchronously using AsyncTask. Let's begin by creating an Android Application project.

Step 01: Build Main Screen

Create a layout for the MainScreen which should have only one button. It should look something like the screenshot below:

Step 02: Add an AsyncTask

Now, we've to add an AsyncTask in MainActivity which should download some data from the internet. As mentioned earlier, you can define an AsyncTask by subclassing it in MainActivity:

	protected class DownloadDataTask extends AsyncTask<String, Void, String>
	{
		@Override
		protected String doInBackground(String... params)
		{
			/* Download your data here */
 
			return null;
		}
	}

Please note:

  1. doInBackground() method is required to implement for this class.
  2. Also, it's important to note that the DownloadDataTask is expecting String… params (i.e., an array of strings) as an input. In our upcoming steps, we'll be passing an array of URLs to AsyncTask.
  3. DownloadDataTask is returning a String value. We can then use this string in some way in our UI thread, whenever this AsyncTask is done executing.

Step 03: Download Data from Internet

To download data from the internet, say from few hard-coded urls, we can make HTTP GET requests. To make a HTTP GET request, we can use objects of DefaultHttpClient and HttpGet class. So, for a given 'url' string, you can have the following implementation:

HTTP GET Example
	DefaultHttpClient client = new DefaultHttpClient();
	HttpGet httpGet = new HttpGet(url);
	try
	{
		HttpResponse execute = client.execute(httpGet);
		InputStream content = execute.getEntity().getContent();
 
		BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
		String s = "";
		while ((s = buffer.readLine()) != null)
		{
			response += s;
		}
	} 
	catch (Exception e)
	{
		e.printStackTrace();
	}

You can use this implementation in doInBackground() method, but for all the URLs passed in the params field.

MainActivity::DownloadDataTask::doInBackground()
		protected String doInBackground(String... params)
		{
			String response = "";
 
			for (String url : params)
			{
				// HTTP CODE
			}
			return response;
 
		}

BUT.. BUT.. BUT. Before moving forward, you should realize that you'll be making network calls from your app. Making a network call is something which your app user may consider fishy, and that's why Android does not allow any app to use Internet by default.

Android Permissions

So, to make a network call, you have to register for a permission to use Internet in AndroidManifest.xml file. Whatever permissions you specify in your AndroidManifest.xml file, they all get reviewed by the users when they installs your app. And, they should see a window like the one below before installing your app from PlayStore.

There are 131 types of permissions you can have for your app. And, out of which the most popular one is android.permission.INTERNET.

You register for a permission in AndroidManifest.xml file using <uses-permission> xml tag as a child of <manifest> xml tag:

AndroidManifest.xml
	<manifest...>
 
		<uses-permission android:name="android.permission.INTERNET" />
 
		<uses-sdk .../>
 
		<application> ... </application>
 
	</manifest>

Step 04: Implementation of Button's click callback

Now, we want to execute this AsyncTask on the click of 'Download Data' button on Main screen. So, let's go ahead and implement its callback in MainActivity. And, within it's implementation, we'll be first creating an object of DownloadDataTask and then executing it while passing an array of web URLs that we want to access it while running DownloadDataTask.

MainActivity::onButtonClick()
	public void onButtonClick(View v)
	{
		String[] urls = new String[] { "http://pages.cs.wisc.edu/~prakhar/android_01.html",
						"http://pages.cs.wisc.edu/~prakhar/android_02.html",
						"http://pages.cs.wisc.edu/~prakhar/android_03.html" };
 
		DownloadDataTask downloadDataTask = new DownloadDataTask();
		downloadDataTask.execute(urls);
	}

Step 05: Use the response (AsyncTask result)

After DownloadDataTask is done with its background processing, it calls its onPostExecute() method and passes the returned-response from doInBackground() method as its argument. So, as you'd have noticed, we collected the text from few webpages in DownloadDataTask and returned it in 'response' variable. Please note that onPostExecute() method of AsyncTask runs on UI thread, so you can make any UI changes in this method. So, let's display that value of 'response' argument in a Toast message, with an implementation like below:

MainActivity::DownloadDataTask::onPostExecute()
@Override
		protected void onPostExecute(String response)
		{
			Toast.makeText(MainActivity.this, response, Toast.LENGTH_LONG).show();
		}

If you launch your app at this point, you'll see the concantenated content from different webpages in a toast message.

Progress Dialog

One common UI feature in an Android device is the “Please wait” dialog that you typically see when an application is performing a long-running task. For example, the application may be logging in to a server before the user is allowed to use it, or it may be doing a calculation before displaying the result to the user. In such cases, it is helpful to display a dialog like the one shown below, known as a ProgressDialog, so that the user is kept in the loop.

So, ProgressDialog are mainly used to avoid a heavy processing blocking your application's main (UI) thread. We can create a progress dialog by creating an object of ProgressDialog class, and set some UI parameters before displaying the dialog.

 
			ProgressDialog mProgressDialog = new ProgressDialog(MainActivity.this);
 
			mProgressDialog.setTitle("Downloading...");
			mProgressDialog.setMessage("Please wait.");
			mProgressDialog.setCancelable(false);
			mProgressDialog.setIndeterminate(true);
			mProgressDialog.show();

We'll now see how we can display this Progress Dialog in our AsyncDownload application, while we're downloading the data.

So, we would want to have such a Progress Dialog just before we start downloading the data. But, since ProgressDialog is a UI component, this is something which we cannot create in doInBackground() method of DownloadDataTask. For our use case, we can override and implement a method of AsyncTask which is called onPreExecute. So, now your DownloadDataTask class implementation should appear something like below:

MainActivity::DownloadDataTask
	ProgressDialog mProgressDialog = null;
 
	protected class DownloadDataTask extends AsyncTask<String, Void, String>
	{
		@Override
		protected String doInBackground(String... params)
		{ /* Same implementation as before */ }
 
		@Override
		protected void onPreExecute()
		{
			// Add your ProgressDialog Implementation here.
		}
 
		@Override
		protected void onPostExecute(String response)
		{
			Toast.makeText(MainActivity.this, response, Toast.LENGTH_LONG).show();
 
			if (mProgressDialog != null)
				mProgressDialog.dismiss();
		}
	}

Please note:

  • We're dismissing the dialog in onPostExecute method, as we'll be done downloading the data at that point.
  • We're defining the mProgressDialog object outside DownloadDataTask class as we'll need to cancel this dialog in case our activity gets killed for some reason.

Now, we're just left with handling a case when our application (thus MainActivity) may get destroyed by the system. In the case, we should also like to dismiss our ProgressDialog if it's still showing up.

MainActivity::onDestory()
@Override
	protected void onDestroy()
	{
		if (mProgressDialog != null)
			mProgressDialog.dismiss();
 
		super.onDestroy();
	}
android-labs-s14/class-08.txt · Last modified: 2014/02/26 12:57 by prakhar