Mobile Application Development and Concurrency Patterns

Mobile Application Development and Concurrency Patterns
Slide Note
Embed
Share

Discover the implications of multiple processes, threads, and Android UI considerations in mobile application development. Learn about communication between UI and background threads, avoiding blocking UI thread, and ensuring thread-safe interactions. Dive into the challenges of displaying animated backgrounds while maintaining UI responsiveness and explore solutions to enhance user experience.

  • Mobile App Dev
  • Concurrency
  • Android Threads
  • UI Responsiveness
  • Thread-Safe

Uploaded on Mar 03, 2025 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.

E N D

Presentation Transcript


  1. CS499 Mobile Application Development Fall 2013 Threads & Android Part 2

  2. Carnegie Mellon Concurrent Programming Multiple processes each logical control flow is a process. Some form of IPC (InterProcess Communication) needed Multiple threads multiple logical control flows within a single process. Shared address space I/O multiplexing event driven concurrency. Shared address space

  3. Android Threads Activities have a main thread the UI thread App components in the same process use the same main thread User interaction, system callback & lifecycle methods are handled in the UI thread It is essential that the UI remains responsive UI toolkit not thread-safe. A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time.

  4. Implications Two rules: 1. Do not block the UI thread Blocking the UI thread hurts responsiveness Long-running operations should be done in background thread 2. Don t access/manipulate the UI toolkit from a non UI thread. Results are unpredictable Q: How can the UI & background threads communicate in a thread-safe manner?

  5. Consider the following Game App: public class Demo extends Activity { int mInFLight; @Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.demo); final View root findViewById(R.id.root); final Drawable bg = root.getBackground(); final TextView msg = (TextView) findViewById(R.id.msg); final Game game = Game.newGame(); ((Button)findViewById(R.id.start)).setOnClickListener( new View.onClickListener() { @Override public void onClick(View v) { // initialize the game! } } }); }

  6. Want to display animated background while waiting to initialize // Broken do not use!!! void initGame(View root, Drawable bg, Game game, TextView resp, String level) { // if animation hasn t started, make it start if (0 >= mInFlight++) { root.setBackgroundResource(R.anim.dots); ((AnimationDrawable)root.getBackground()).start(); } // initialize the game String msg = game.initialize(level); // if last initialization remove & clean up animation if (0 >= --mInFlight) { ((AnimationDrawable)root.getBackground()).stop(); root.setBackgroundDrawable(bg); } resp.setText(msg); }

  7. Problem: UI thread is executing game.initialize and hence can t show the animation background animation won t work if the initialize takes too long, the android framework will suspend it

  8. Might be tempted to do the following: public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); } Seems to meet Rule 1 but violates Rule 2

  9. Solution: AsyncTask Structured, safe, powerful, and easy-to-use way to manage UI and background threads correctly. Generic class class AsyncTask<Params,Progress,Result> { } Generic type parameters Params Types used in background work Progress Types used when indicating progress Result Types of result

  10. AsyncTask methods void onPreExecute() Runs before doInBackground() Result doInBackground(Params params) Performs work can call void publishProgress(Progress values) void onProgressUpdate(Progress values) Invoked in response to publishProgress() void onPostExecute(Result result) Runs after doInBackground()

  11. public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }

  12. AsyncTask Created on UI thread. When execute d 1. UI thread invokes onPreExecute() to do any setup 2. UI thread creates new background thread that runs a doInBackground() method concurrently 3. Once background thread terminates, onPostExecute() is run in the UI thread.

  13. public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main); final View dots = findViewById(R.id.dots); final Drawable bg = dots.getBackground(); final TextView msg = ((TextView) findViewById(R.id.msg)); final Game game = Game.newGame(); ((Button) findViewById(R.id.start)).setOnClickListener( new View.OnClickListener() { public void onClick(View v) { new AsyncInitGame(dots, bg, game,msg).execute("basic"); } }); }

  14. private final class AsyncInitGame extends AsyncTask<String, Void, String> { private final View root; private final Game game; private final TextView message; private final Drawable bg; public AsyncInitGame(View root, Drawable bg, Game game, TextView msg) { this.root = root; this.bg = bg; this.game = game; this. message = msg; } // runs on the UI thread @Override protected void onPreExecute() { if (0 >= mInFlight++) { root.setBackgroundResource(R.anim.dots); ((AnimationDrawable)root.getBackground()).start(); } }

  15. // runs on the UI thread @Override protected void onPostExecute(String msg) { if (0 >= --mInFlight) { ((AnimationDrawable)root.getBackground()).stop(); root.setBackgroundDrawable(bg); } message.setText(msg); } // runs on a background thread @Override protected String doInBackground(String args) { return ((1 != args.length) || (null == args[0])) ? null : game.initialize(args[0]); } }

  16. AsyncTask dataflow New Thread Creation Thread (UI) execute(args ) doInBackground(args ) : result doPostExecute(result)

  17. private final class LoadIconTask extends Integer,Integer,Bitmap> { protected Bitmap doInBackground(Integer resid) { Bitmap tmp = BitmapFactory.decodeResource( getResources(),resid[0]); for (int I = 1; i< 11;i++) { // some long running op publishProgress(i*10); } return tmp; } protected void onProgressUpdate(Integer... values) { progress.setProgress(values[0]); } protected void onPostExecute(Bitmap result) { iview.setImageBitmap(result); } }

  18. Be careful! UI thread should not modify data sent as parameters to AsyncTasks doInBackground() must only make thread-safe references to variables inherited into its scope. int mCount; public void initButton(Button button) { mCount = 0; new View.OnClickListener() { @Override public void onClick(View v) { new AsyncTask<Void,Void,Void>() { @Override protected Void doInBackground(Void args) { mCount++; // NOT THREAD SAFE return null; } }.execute(); } }); }

  19. However Beginning with Honeycomb (3.0), Android changed how it's running the AsyncTasks: AsyncTask task = new AsyncTask1(); if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { task.executeOnExecutor(AsyncTask.THREAD_P OOL_EXECUTOR); } else { task.execute(); } http://www.techrepublic.com/blog/android-app- builder/android-asynctask-behavior-changes-you- should-know/

  20. Handler Threads can also communicate by posting Messages & Runnables to a Handler Message Can contain a code, data object & args Recipient (Handler) implements response Runnable Contains an instance of the Runnable interface Sender implements response

  21. Handler sendMessage() Puts Message on MessageQueue post() Puts runnable on Message Queue Looper One per Thread Dispatches MessageQueue entries Calls handleMessage() for Messages Calls run() for Runnables

  22. Simple use of UI handler public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); }

  23. Handler Two main uses for a Handler Schedule Message/Runnable for future execution Enqueue action to be performed on a different thread

  24. Messages & Handlers Create Message and set Message content Handler.obtainMessage() Message.obtain() Many variants. See documentation Message parameters include int arg1, arg2 int what Object obj Bundle data

  25. Messages & Handlers sendMessage() Queue message immediately sendMessageAtFrontOfQueue() Insert message immediately at front ofqueue SendMessageAtTime() Queue message at the stated time sendMessageDelayed() Queue message after the stated delay

  26. public class SimpleThreadingExample extends Activity { private final static int SET_PROGRESS_BAR_VISIBILITY = 0; private final static int PROGRESS_UPDATE = 1; private final static int SET_BITMAP = 2; private ImageView iview; private ProgressBar progress; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case SET_PROGRESS_BAR_VISIBILITY: { progress.setVisibility((Integer) msg.obj); break; } case PROGRESS_UPDATE: { progress.setProgress((Integer) msg.obj); break; } case SET_BITMAP: { iview.setImageBitmap((Bitmap) msg.obj); break; } } } };

  27. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); iview = (ImageView) findViewById(R.id.imageView1); progress = (ProgressBar) findViewById(R.id.progressBar1); final Button button = (Button) findViewById(R.id.loadButton); button.setOnClickListener(new OnClickListener() { public void onClick(View v) { new Thread(new LoadIconTask(R.drawable.icon, handler)).start(); } }); }

  28. private class LoadIconTask implements Runnable { private final int resId; private final Handler handler; LoadIconTask(int resId, Handler handler) { this.resId = resId; this.handler = handler; } Message msg = null; msg = handler.obtainMessage(SET_PROGRESS_BAR_VISIBILITY,ProgressBar.VISIBLE); handler.sendMessage(msg); for (int i = 1; i < 11; i++) { sleep(); msg = handler.obtainMessage(PROGRESS_UPDATE, i * 10); handler.sendMessage(msg); } msg = handler.obtainMessage(SET_BITMAP, tmp); handler.sendMessage(msg); msg = handler.obtainMessage(SET_PROGRESS_BAR_VISIBILITY, ProgressBar.INVISIBLE); handler.sendMessage(msg); } private void sleep() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace();} } } } public void run() { final Bitmap tmp = BitmapFactory.decodeResource(getResources(), resId);

  29. Runnables & Handlers boolean post(Runnable r) Add Runnable to the MessageQueue boolean postAtTime(Runnable r, long uptimeMillis) Add Runnable to the MessageQueue. Run at a specific time (based on SystemClock.upTimeMillis()) boolean postDelayed(Runnable, long delayMillis) Add Runnable to the message queue. Run after the specified amount of time elapses.

  30. public class SimpleThreadingExample extends Activity { private ImageView iview; private final Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); iview = (ImageView) findViewById(R.id.imageView1); final Button button = (Button) findViewById(R.id.loadButton); button.setOnClickListener(new OnClickListener() { public void onClick(View v) { new Thread(new LoadIconTask(R.drawable.icon)).start();} }); } private class LoadIconTask implements Runnable { int resId; LoadIconTask(int resId) { this.resId = resId; } public void run() { final Bitmap tmp = BitmapFactory.decodeResource(getResources(),resId); handler.post(new Runnable() { @Override public void run() { iview.setImageBitmap(tmp);} }); }} }

  31. public class SimpleThreadingExample extends Activity { private Bitmap bitmap; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final ImageView iview = (ImageView) findViewById(R.id.imageView1); final Button button = (Button) findViewById(R.id.loadButton); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { public void run() { bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.icon); iview.post(new Runnable() { public void run() { iview.setImageBitmap(bitmap); } });} }).start(); } }); } }

  32. public class SimpleThreadingExample extends Activity { private Bitmap bitmap; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final ImageView iview = (ImageView) findViewById(R.id.imageView1); final Button button = (Button) findViewById(R.id.loadButton); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { public void run() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon); SimpleThreadingExample.this.runOnUiThread(new Runnable() { public void run() { iview.setImageBitmap(bitmap); } }); } }).start(); } }); } }

More Related Content