AntiPattern: freezing a UI with Broadcast Receiver

The worst thing that can happen to your app's responsiveness is an "Application Not Responding" (ANR) dialog.

In android guidelines we can read:

In any situation in which your app performs a potentially lengthy operation, you should not perform the work on the UI thread, but instead create a worker thread and do most of the work there. This keeps the UI thread (which drives the user interface event loop) running and prevents the system from concluding that your code has frozen.

It's very important to understand what is Main Thread.

When an Android app is launched, the system creates a thread for the application which is known as the main thread. If you run up any project under the debugger in Eclipse you will see the main thread.

If your app ties up the UI thread for more than 5 seconds or a BroadcastReceiver does not complete within 10 seconds, Android will throw up the Application Not Responding (ANR) dialog which gives the user the opportunity to quit your app.

The best way to figure things out, you experience them.

Here you can find an example OF WHAT NOT TO DO WITH A BROADCAST RECEIVER.

When we register a receiver with registerReceiver (BroadcastReceiver receiver, IntentFilter filter) we must always remember that the BroadcastReceiver will run in the main activity thread.
This means that our activity is blocked until it ends the method OnReceive ()

In our BAD example (intentionally bad) we will register a Broadcast programmatically.
/**
 * This is an example of WHAT NOT TO DO !!
 *
 */
public class FreezingActivity extends SherlockActivity {

 protected FreezingUIReceiver mReceiver = null;

    private static final String TAG = "FreezingActivity";
    protected static final String FREEZYACTION =
          "it.gmariotti.android.examples.antipattern.FREEZING_ACTION";

    @Override
    protected void onPause() {
       super.onPause();
       unregisterReceiver(mReceiver); //take care to unregister
    }

    @Override
    protected void onResume() {
       super.onResume();
       mReceiver = new FreezingUIReceiver();
       registerReceiver(mReceiver, new IntentFilter(FREEZYACTION));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.simple);
       getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

   /**
    * 
    */
    private void newEvent() {
       Toast.makeText(this, getString(R.string.text_newevent), 1000).show();
    }

   /**
    * Launch Intent Broadcast
    */
    private void launchBroadcast() {
       Toast.makeText(this, getString(R.string.text_broadcast), 1000).show();
       Intent i = new Intent(FREEZYACTION);
       sendBroadcast(i);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
       getSupportMenuInflater().inflate(R.menu.freezing_menu, menu);
       return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
       switch (item.getItemId()) {
          case android.R.id.home:
             NavUtils.navigateUpFromSameTask(this);
             return true;
          case R.id.menu_refresh:
             launchBroadcast();
             return true;
          case R.id.menu_newevent:
             newEvent();
             return true; 
       }
       return super.onOptionsItemSelected(item);
    }
}
Dynamically registered receivers are called on the UI thread. This means that your receivers blocks any UI handling.
In our example we will take "a long running task".
public class FreezingUIReceiver extends BroadcastReceiver {

    private static final String TAG = "FreezyUIReceiver";
    public FreezingUIReceiver() {}
  
    @Override
    public void onReceive(Context context, Intent intent) {

       Log.d(TAG, "onReceive FREEZING RECEIVER");
  
       //This is an example of WHAT NOT TO DO !!
       try{
          Thread.sleep(15000);
       }catch(Exception e){}
  
       Log.d(TAG, "FREEZING RECEIVER END");
    } 
}
Launch app, click refresh button and the freezing up of the UI is gone!

Here we can see where runs our receiver:

...."Application Not Responding" (ANR) dialog...

You should avoid any long tasks in your BroadcastReceiver.
For any longer tasks you should start a service from within your receiver.


You can get code from GitHub:

Popular posts from this blog

Expand and collapse animation

How to centralize the support libraries dependencies in gradle

A Material styled AlertDialog