Android:Exercises Thread

From Juneday education
Jump to: navigation, search

The Great Pretender

In this exercise we will create a rather useless class that specialises in pretending to work. The idea is to:

  • pretend to work for a specified amount of seconds
  • notify the GUI when done

Create an Activity

The Activity shall have:

  • EditText - for entering the number of seconds to pretend to work
  • a Button ("Start") to start the pretention

Note: you could add android:inputType="numberSigned" to the EditText in the Layout file. This makes the EditText make sure that the text can only be a number. This kind of check might not always be enough, but in this case it will do fine.

Suggested solution

Expand using link to the right to see the full content.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  tools:context="se.juneday.throwaway.greatpretender.MainActivity">

  <EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="numberSigned" 
    android:id="@+id/work_peroid_input"/>

  <Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/startButton"
    android:text="@string/start_text"/>

</LinearLayout>

and as you can see we need a String resource:

res/values/strings.xml:

<resources>
  <string name="app_name">GreatPretender</string>
  <string name="start_text">Start</string>
  <string name="working_text">Working hard</string>
</resources>

React on clicking "Start"

When the use clicks on the "Start" Button:

  • the Button's text shall be changed to "Working hard"
  • write a short message to Log

Suggested solution

Expand using link to the right to see the full content.

activity_main.xml:

<source lang="xml" highlight=15>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  tools:context="se.juneday.throwaway.greatpretender.MainActivity">

  <EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="numberSigned"
    android:id="@+id/work_peroid_input"/>

  <Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/start_text"
    android:id="@+id/startButton"
    android:onClick="startClicked"/>

</LinearLayout>

and MainActivity.java:

package se.juneday.throwaway.greatpretender;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

  private static final String LOG_TAG = MainActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(LOG_TAG, "onCreate");
  }

  public void startClicked(View v) {
    Log.d(LOG_TAG, "startClicked");
    ((Button)findViewById(R.id.startButton)).setText(R.string.working_text);
  }

}

Create an inner class extending AsyncTask

Create an inner class that extends AsyncTask. The class shall override the method doInBackground. The method shall log that it has been started.

For now, we need not do anything to indicate progress or when done. This means we can skip the second and third parameters when defining the class. We do this by passing Void (not void).

Suggested solution

Expand using link to the right to see the full content.

MainActivity.java:

package se.juneday.throwaway.greatpretender;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

  private static final String LOG_TAG = MainActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(LOG_TAG, "onCreate");
  }

  public void startClicked(View v) {
    Log.d(LOG_TAG, "startClicked");
    ((Button)findViewById(R.id.startButton)).setText(R.string.working_text);
  }

  private class PretendToWork extends AsyncTask<Integer, Void, Void> {
    
    @Override
    protected Void doInBackground(Integer... integers) {
      Log.d(LOG_TAG, "doInBackground");
      return null;
    }

  }

}

More reactions on clicking "Start"

When the use clicks on the "Start" Button you shold now also:

  • create and execute and AsyncTask instance
  • the number written in the EditText is passed as parameter to doInBackground

Hint: use the log the make sure that the method is invoked.

Suggested solution

Expand using link to the right to see the full content.

MainActivity.java:

package se.juneday.throwaway.greatpretender;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

  private static final String LOG_TAG = MainActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(LOG_TAG, "onCreate");
  }

  public void startClicked(View v) {
    Log.d(LOG_TAG, "startClicked");
    ((Button)findViewById(R.id.startButton)).setText(R.string.working_text);
    int seconds; // don't default to anything
    EditText et = ((EditText)(findViewById(R.id.work_peroid_input)));
    try {
      seconds = Integer.parseInt(et.getText().toString());
    } catch (NumberFormatException e) {
      Log.d(LOG_TAG, "Bad input, defaulting to 0");
      seconds = 0;
    }
    new PretendToWork().execute(seconds);
  }

  private class PretendToWork extends AsyncTask<Integer, Void, Void> {

    @Override
    protected Void doInBackground(Integer... integers) {
      Log.d(LOG_TAG, "doInBackground: " + integers[0]);
      return null;
    }

  }

}

When done...

When the pretended work is done you should update the Button's text to "Start" again and also Log the number of seconds slept. The number of seconds slept shall be given as a parameter to onPostExecute from the method doInBackground.

Hint

Expand using link to the right to see a hint on how to sleep.

MainActivity.java:

      while (seconds --> 0) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Log.d(LOG_TAG, "doInBackground: " + seconds + " seconds left");
      }

assuming that seconds is a variable representing the number of seconds to sleep.


Suggested solution

Expand using link to the right to see the full content.

MainActivity.java:

package se.juneday.throwaway.greatpretender;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

  private static final String LOG_TAG = MainActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(LOG_TAG, "onCreate");
  }

  public void startClicked(View v) {
    Log.d(LOG_TAG, "startClicked");
    ((Button)findViewById(R.id.startButton)).setText(R.string.working_text);
    int seconds; // don't default to anything
    EditText et = ((EditText)(findViewById(R.id.work_peroid_input)));
    try {
      seconds = Integer.parseInt(et.getText().toString());
    } catch (NumberFormatException e) {
      Log.d(LOG_TAG, "Bad input, defaulting to 0");
      seconds = 0;
    }
    new PretendToWork().execute(seconds);
  }

  private class PretendToWork extends AsyncTask<Integer, Void, Integer> {

    @Override
    protected Integer doInBackground(Integer... integers) {
      int seconds=integers[0];
      Log.d(LOG_TAG, "doInBackground: " + seconds);
      while (seconds-->0) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Log.d(LOG_TAG, "doInBackground: " + seconds + " seconds left");
      }
      Log.d(LOG_TAG, "doInBackground: leaving");
      return integers[0];
    }

    @Override
    protected void onPostExecute(Integer seconds) {
      super.onPostExecute(seconds);
      Log.d(LOG_TAG, "onPostExecute: " + seconds);
      ((Button)findViewById(R.id.startButton)).setText(R.string.start_text);
    }

  }

}

When doing ...

During the pretention work you should update the Button with the text "Working hard (2 seconds left)". 2 should of course be replaced by the number of seconds remaining.

Suggested solution

Expand using link to the right to see the full content.

MainActivity.java:

package se.juneday.throwaway.greatpretender;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

  private static final String LOG_TAG = MainActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(LOG_TAG, "onCreate");
  }

  public void startClicked(View v) {
    Log.d(LOG_TAG, "startClicked");
    ((Button)findViewById(R.id.startButton)).setText(R.string.working_text);
    int seconds; // don't default to anything
    EditText et = ((EditText)(findViewById(R.id.work_peroid_input)));
    try {
      seconds = Integer.parseInt(et.getText().toString());
    } catch (NumberFormatException e) {
      Log.d(LOG_TAG, "Bad input, defaulting to 0");
      seconds = 0;
    }
    new PretendToWork().execute(seconds);
  }

  private class PretendToWork extends AsyncTask<Integer, Integer , Integer> {

    @Override
    protected Integer doInBackground(Integer... integers) {
      int seconds=integers[0];
      Log.d(LOG_TAG, "doInBackground: " + seconds);
      while (seconds-->0) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Log.d(LOG_TAG, "doInBackground: " + seconds + " seconds left");
        publishProgress(seconds);
      }
      Log.d(LOG_TAG, "doInBackground: leaving");
      return integers[0];
    }

    @Override
    protected void onPostExecute(Integer seconds) {
      super.onPostExecute(seconds);
      Log.d(LOG_TAG, "onPostExecute: " + seconds);
      ((Button)findViewById(R.id.startButton)).setText(R.string.start_text);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
      super.onProgressUpdate(values);
      int timeLeft = values[0];
      Log.d(LOG_TAG, "onPostExecute: " + timeLeft);
      ((Button)findViewById(R.id.startButton)).setText(getString(R.string.working_text) + " (" + timeLeft + " seconds left)" );
    }
  }

}



Chapter links

External links


Source code

The complete source code of the solution suggestions above can be found in the reopsitory GreatPretender at github. The two most interesting files are listed below with direct links to github:

Android - the practical way

Book TOC | previous chapter | next chapter