Threads

// BAD USAGE: function call to time-consuming // function causes main thread to hang play_music();

Figure 3.1 An example of the message that displays when a thread hangs.

This means any user request such as navigating back to the home screen or multiple pushes of an onscreen button are not registered until the music is completely finished playing. The unresponsive UI might even cause the Android system to show an error such as the previous one in Figure 3.1.

This is resolved by launching a secondary thread to call the play_music() function. The steps to do this are

1. Create a new thread to hold a Runnable object: Thread initBkgdThread = new Thread(

//insert runnable object here

2. Create a Runnable object that overrides the run() method to call the time-consuming task:

Start the thread, which then runs the task: initBkgdThread.start();

The setup of the secondary thread to contain the time-consuming task is quick, so the main thread can continue servicing other events.

Before showing the code for the full activity, the supporting files are discussed. Media playback is covered more fully in Chapter 6,"Multimedia Techniques," but for illustration, the song is implemented here as a sequence of notes specified using ring-tone text transfer language (RTTTL). For example, the RTTTL code describing a quarter note of the A(220Hz) just below middle C is shown in Listing 3.1. Putting this in a single-line text file in the res/raw/ directory registers it as the R.raw.a4 resource.

Listing 3.1 RTTTL file res/raw/a4.rtttl, which denotes A just below middle-C.

Then, a call in the activity to the media player plays this ring-tone note:

m_mediaPlayer = MediaPlayer.create(this, R.raw.a4); m_mediaPlayer.start();

This recipe uses four different notes in four separate RTTTL files: g4.rtttl, a4.rtttl, b4.rtttl, and c5.rtttl. These are just exact copies of Listing 3.1 with the a4 changed in the file to reflect the new note in each case, but it can also be expanded to other notes or formats.

One aside is that the MediaPlayer launches its own background thread to play the media. So, if this was a single longer file to play, it is possible to avoid the use of an explicit thread as explained in Chapter 6. That fact does not help when multiple files need to be played quickly, as here, but it is important to know that threads are not always necessary.

The trigger for starting the music is a button press.The Button widget needs to be specified in the main layout file (here called main.xml) and is identified with the name trigger, as shown in Listing 3.2.

Listing 3.2 res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent" >

<Button android:id="@+id/trigger"

android:layout_width="100dip" android:layout_height="100dip" android:text="Press Me"

</LinearLayout>

One side-effect of launching a separate thread is that it still continues even if the main activity is paused. This is seen by implementing the background thread and navigating back to the home screen during music play. The music keeps playing until it is completed. If this is not the preferred behavior, the play_music() function can check a flag (here called paused), which is set during the main activity's onPause() function to stop music playback when the main thread is paused.

All the previous items are combined into the full activity PressAndPlay in Listing 3.3.

Listing 3.3 src/com/cookbook/launch_thread/PressAndPlay.java package com.cookbook.launch_thread;

import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.view.View; import android.widget.Button;

public class PressAndPlay extends Activity {

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

Button startButton = (Button) findViewById(R.id.trigger); startButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view){

//standalone play_music() function call causes //main thread to hang. Instead, create //separate thread for time-consuming task Thread initBkgdThread = new Thread(new Runnable() { public void run() { play_music();

initBkgdThread.start();

int[] notes = {R.raw.c5, R.raw.b4, R.raw.a4, R.raw.g4}; int NOTE_DURATION = 400; //millisec MediaPlayer m_mediaPlayer; private void play_music() {

//check to ensure main activity not paused if(!paused) {

if(m_mediaPlayer != null) {m_mediaPlayer.release();} m_mediaPlayer = MediaPlayer.create(this, notes[ii%4]); m_mediaPlayer.start(); try {

Thread.sleep(NOTE_DURATION); } catch (InterruptedException e) { e.printStackTrace();

boolean paused = false;

@Override protected void onPause() { paused = true; super.onPause();

@Override protected void onResume() { super.onResume(); paused = false;

Note the Thread.sleep() method pauses the thread for approximately the amount specified (in milliseconds).This is used to implement the note duration.

Also note the convention used in the lifecycle methods: Additional activity-specific logic is bracketed by the super methods.This is good practice to ensure proper completion of commands. So the internal pause flag is set to true before truly pausing the activity, and the activity is fully resumed before setting the internal pause flag to false.

Character Building Thought Power

Character Building Thought Power

Character-Building Thought Power by Ralph Waldo Trine. Ralph draws a distinct line between bad and good habits. In this book, every effort is made by the writer to explain what comprises good habits and why every one needs it early in life. It draws the conclusion that habits nurtured in early life concretize into impulses in future for the good or bad of the subject.

Get My Free Ebook


Post a comment