CS 491 Lecture 3 – Media and Resource Management
Before This Class
A
Before next lecture:
- Watch http://youtu.be/XQOk5Z-c3yQ.
- Watch http://youtu.be/HhgOh3odhYU.
- Skim the SoundPool API (http://developer.android.com/reference/android/media/SoundPool.html) for playing sounds simultaneously.
Hey, A. Today we’re going to explore getting Android to play sounds and show pictures. Many of the killer mobile apps these days are rich with media, and in order to reach our first million, we’ll have to know a thing or two about adding this glitz.
Let’s start with sound. I’ve got a mostly empty project here, with just an Activity. Here’s my first goal:
- Play a sound when I tap on the screen.
Activity has an onTouchEvent just like View. As someone mentioned in the last class, if you are versed in the model-view-controller design pattern, you might be bothered that two UI (Activity and View) classes handle their own events. It’d be nice to have the handlers pushed out to some intermediate controller class. Good design is a big task. Let’s not tackle it now.
Let’s play an audio file from the web. One class I can use for that is MediaPlayer. The easiest way to make one is to call static method create. We’ve got to give it our context, like most objects that we create, and a path to our sound file. The interface doesn’t allow for a plain String. It requires a URI, which might be an Internet address, a path to a file on the SD card, or various other data sources. Ours is just a URL, which we can turn turn into a Uri with Uri.parse.
To get it to play, we call the start method.
Here’s an experiment for you to run. When I put code after start(), when is it executed? Answer that question for yourself.
[break]
start() is a non-blocking call. It returns control even before the work of the method finishes. Behind the scenes, it must fire up some thread that runs concurrently with our main thread. This means that even though our player is a local variable, it’s still sitting in memory. We can set up a callback via setOnCompletionListener to free it.
If we fail to do this, we may run out of memory if we play a lot of audio files.
Let’s now move on to showing a picture. There are standard widgets for displaying images, like JLabel in Swing, but we’re going to stick with Canvas for one more day. Let’s make a new View subclass just like we did the other day.
This time, in onDraw, we want to draw a picture. Canvas has several drawBitmap methods. The first parameter is a bitmap, which we don’t have yet. The second are the top-left coordinates where we want the bitmap to appear. The final is a Paint object. For bitmaps, this can be null. If we wanted to apply some sort of to the bitmap, we’d use paint to do so. We’ll use null for now and place our image at the top-left. Android, like most GUI libraries but unlike your calculator, considers the origin to be the top-left of the screen. For the bitmap, let’s use the icon for our app. The icon is what’s called a resource. That’s what your partner is learning about. For our purposes, we can load in a bitmap resource with the BitmapFactory class.
One caveat here, which applies to any drawing code. Don’t do I/O or other heavyweight operations in the drawing callback. Try to avoid anything with the new operator. Here, let’s read once in the constructor and store the result to an instance variable.
B
Before next lecture:
- Watch http://youtu.be/xEiIzX_hEQ0.
- Skim the Resources Overview (http://developer.android.com/guide/topics/resources/overview.html).
B, you drew the resources straw. Today you and I are going to discuss the res directory you see in your project.
Instead of starting with a definition, let’s start with the problem resources solve. Suppose you want to pop up a message when a user resumes an app. You could use a little Toast message. The gotcha is that our literal String here only works for one language. We could fix that by creating a conditional ladder: if we have a Spanish locale, use hola; if French, use bonjour; if Japanese, konnichiwa. While this would work, that’s a lot of mindless coding.
A good software system, be it Android or otherwise, solves this problem by externalizing items like these labels to resources, which are stored not in code but in the res directory. We let the framework decide which resource to draw up based on the device’s configuration, and we just refer to the greeting symbolically. We write no repetitive conditional code.
String resources like our greeting go in the values directory, in strings.xml. We make a new element for our resource, name it, and in our code, we conjure it up with getString(name). That worked for the English version, but what about Spanish? For that, we need a second values directory, this time flagged with -es, for Espanol. We make a custom strings.xml and redefine our resources to their Spanish counterparts.
Now, if we switch our locale, our device’s configuration gets changed. By default, when the configuration changes, our activity is brought down and recreated, which lets the new correct resources get pulled out from the right resource subdirectories.
The next resource we’ll look at is a menu. Menus popup when we hit the menu button in an Activity that overrides a certain callback. Let’s override that method and another one that will get called when a menu item is selected.
Our menu is defined not programmatically, but as an XML resource. We’ll be able to label menu items with string resources. Let’s create two items, one to quit our app and one that will pop up a dialog box. We also need to give each item an identifier, whose role will be seen shortly. The syntax is a little strange.
In-class
With your partner, implement a bubble-wrap popping app. Follow these guidelines:
- Use these sound and drawable resources.
- Sound resources go in a raw subdirectory in res.
- Create a custom canvas and drawing routine.
- Let the user tap to pop bubbles.
- Talk to each other.
Your questions
- Why not Swing? “The biggest problem with Swing was that it can do so much that it’s kind of become like the 747 cockpit if you like APIs. There’s a lot of complexity there, and the hard part is figuring out how to use it” – James Gosling
- Best place to do lengthy calculations? It depends. Attached to UI? AsyncTask. Not attached? A thread.
- View built on Swing? No, Skia. http://atnan.com/blog/2011/11/10/ios-vs-android-ics-hardware-accelerated-graphics-pipelines/
- Multitouch on emulator? http://tools.android.com/tips/hardware-emulation
- 6.0f? A float literal. A double might be okay on modern hardware. http://developer.android.com/guide/practices/performance.html#avoidfloat
- Gingerbread of ICS? http://developer.android.com/about/dashboards/index.html
- What is savedInstanceState? A hash for storing UI data that you want to persist across Activity lifecycle events. Not user data, but ephemeral things like selections, scrolling position, etc. http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState%28android.os.Bundle%29