MEIC/MERC 2015/2016
Mobile and Ubiquitous Computing
Lab Guide 3
Multithreading and Storage in Android
Objectives:
- Introduction to multithreading in Android: UI thread, worker threads, and AsyncTasks.
- Overview of the main Android mechanisms for local data storage: files and databases.
Material:
Exercise I – Threads and Concurrency
The goal of this exercise is introduce you to the behavior of UI thread, worker threads, and AsyncTasks.
1. Basic threading
a) Create an Android application with a single activity and a time ticker thread running in background. The worker thread maintains an internal counter which is incremented every second. For each iteration, the counter value must be printed in the LogCat debugging console. Add two buttons to the main activity: "Start" and "Stop". The "Start" button must reset the counter and start counting. The "Stop" button must stop counting. Tips:
- Use
Log.d()
to print the ticking messages. To enable LogCat to filter messages based on the application package name, make sure to set the Android Studio optionTools > Android > Enable ADB Integration
. - Use
Thread.sleep()
to block the worker thread for a configurable about of time. - To stop the worker thread from the main thread, invoke the
interrupt()
method of the worker thread and return from the worker thread's loop if an interrupt occurs.
b) Inspect the threads of your application using the debugger. Run the app on the emulator, press the "Start" button, and open the Android Device Monitor (ADM). On the "Devices" panel of ADM (to the left hand side), select your application ID, and click the icon "Update threads" in the top of the panel. Identify the main thread and the worker thread running. Then press the "Stop" button. What happened to the threads?
2. Message passing between threads
a) Import the project SimpleImageDownload.tgz into Android Studio. Compile it, test it on the emulator, and study its code. What does this application do?
b) Modify this application so that the status messages currently printed in the LogCat console are written instead in the UI (below the "Download File" button). Use a handler to enable the worker thread to communicate with the UI thread. Follow the steps described next and explain what this code does:
- Create a
Handler
object and bind it to the UI thread. For this, in the main activity, add the following line:
private Handler handler = new Handler(this);
Handler.Callback
interface in the Activity
. First, modify the activity signature with the qualifier "implements Handler.Callback
", then add the following callback implementation to the activity:public boolean handleMessage(Message msg) { String text = msg.getData().getString("status"); TextView statusText = (TextView) findViewById(R.id.status); statusText.setText(text); return true; }
Log.i
with calls to sendMessage
and implement the sendMessage
method:private void sendMessage(String what) { Bundle bundle = new Bundle(); bundle.putString("status", what); Message message = new Message(); message.setData(bundle); handler.sendMessage(message); }
3. Async tasks
Modify the project SimpleImageDownload in order to download the image using an AsyncTask
and show the downloaded image on an ImageView
widget. To show the downloaded image in the ImageView
, use the method setImageBitmap()
. Create an AsyncTask as shown in the reference manual and override method onPostExecute() to update the image view. Tip: Use the following skeleton for your AsyncTask
:
public class DownloadTask extends AsyncTask{ public DownloadTask(ImageView imageView, TextView statusText) { ... } @Override protected void onPreExecute() { ... } @Override protected Bitmap doInBackground(String... inputUrls) { ... } @Override protected void onPostExecute(Bitmap result) { ... } }
4. Custom message loops
Learn more about message queues and communication between threads by studying a simple implementation of the classic producers-consumers problem. Import the project ProducerConsumerWithLooper.tgz into Android Studio. Compile it, execute it, and understand its code. Interpret the output of the program in the LogCat console. In the source code, what's the role of the handler
variable? What's a Looper
?
Exercise II – Local Data Storage
1. Reading and writing files
Download and import the project FileExplorer.tgz into Android Studio. Build the project and run it on the emulator. This application will help you to understand how to read and write files in both internal and external local storage.
a) Launch the application and press the button "Use Internal Storage". Write some text and hit "Write". Then, tap the "Read" button. To make sure that the text is stored persistently, terminate and restart the application. Next, in the main activity, select the same option as before and tap the "Read" button. You should see your text. Explain how the application is performing the read and write file operations by studying the source code of the application. What is the name of the data file accessed by the application?
b) Locate the data file in the file system of the emulator using the adb tool. Proceed as follows:
- Open a console and add the path of the adb tool to the
PATH
environment variable. The path to adb is<sdk>/platform-tools
. To figure out the location of the<sdk>
directory, select "File > Project Structure" in Android Studio. - Obtain the name of your emulator by executing the command:
adb devices
. - Open a shell to the emulator. Assuming that the emulator name is "emulator-5554", run the command:
adb -s emulator-5554 shell
. - You are now logged in the emulator. You can execute the commands of a typical Linux console. Locate the application data file in the path:
data/data/<package>/files
. Use the package name of the application. Check that the content of the data file matches your input text.
c) Repeat both these questions for external storage. In the main activity select instead the option "Use External Storage". Note that if you are using an emulator for this example you'll have to make sure that you created an SD card for the instance you're working with. Use the adb to locate the file in the SD card. The path is /sdcard/Android/data/<package>/files
.
d) In our file-handling example activities you may notice a subtle potential problem. We're performing I/O operations from the main UI thread. This is almost never a good idea. Reading and writing data to and from filesystem, internal or external, can block the main UI thread. Fix this problem by performing I/O this from a Thread or an AsyncTask.
2. Shared preferences files and SQLite databases
Investigate two additional Android storage mechanisms – shared preferences files and SQLite databases – by reading "Using Shared Preferences" and "Using Databases" from the official Android documentation.