How I solved my Android ANRs…


What’s an ANR in Android?

Its when your application does not respond, it hangs, and eventually Android will display a prompt to the user to either force quit the app, or wait until the application responds. This is usually due to some thread or process in a waiting state, or the main UI thread is doing too much work that could be done in another thread, or an AsyncTask.

The context

For me, one of my applications kept getting more reports of crashing and ANRs on the Developer Console. When that happens, reviews and ratings take a dip. So I had to do a lot of research, and debugging as to what can cause it, and review my app logic.

The app in terms of flow itself is relatively simple, it processes a URL, and grabs data, such as text, from the URL resource, and later processes that data. It also, depending on the context, uses RESTful API services to process URL and text, and return a result. Obviously, this is quite a job to perform, and the latency, plus data sizes returned can add to the user experience of how the app flows in terms of performance.

The problem

Backing up a little, we know that an ANR happens when we’re doing too much work on a thread, or the main UI thread, gets put in a waiting state, and so the Activity becomes unresponsive and the prompt is displayed.

When a user clicked a button to process a URL resource, I had many things going on, such as calling on an AsyncTask to do the network operations, and also doing text processing afterwards.

public void SendToNextScreen(View v) {
    //when the button is clicked, this logic executes
    Intent nextActivity = new Intent(this, ResultActivity.class);

    //this ran on a separate Thread
    String fullResourceData = ParseUrlResourceNewThread(); 

    //this ran on an AsyncTask
    String processedData = ProcessRawDataAsync(); 

    nextActivity.putExtra("Key", processedData);

    startActivity(nextActivity);
}

Taking a look at that piece of code line by line, here’s what was going on when a user clicked a button:

  1. Line 6: Launch a new Runnable Thread, that will perform network tasks and return some volume of data, in this case its in String format.
  2. Line 9: Once the thread was done executing, launch an AsyncTask thread to process the raw data returned from the Thread from Line 6.
  3. Line 11: Pass the processed data as an extra Bundle before starting the new activity.

The flaw in this approach was, that piece of code, that method, was executing on the main UI thread. It became unresponsive until each line was finished executing.

This took a heavy toll on not just the main UI thread having to wait for network jobs to finish, but also the user experience did not feel good, and contributed to other crashes. Tracing these errors meant looking at the threads on the IDE to see what was being executed when to make sense of the execution flow and where it hangs.

The way I setup that piece of code; in the XML front-end of the app, I had the android:onClick event handler pointing to that method. The main UI was doing too much, and so, the app hanged, became unresponsive, and eventually crashed or force quit.

How I solved it

By using both AsyncTask and Thread together, and setting up an OnClickListener for the button onClick event handler. Here’s what I mean.

I was already using an AsyncTask thread in my logic already, but I felt that it was not used effectively, in addition, I wanted a ProgressBar to display during Thread execution when network tasks were being done. One way of doing this was to use an AsyncTask. You can learn more in this article: https://www.concretepage.com/android/android-asynctask-example-with-progress-bar

But what made using the AsyncTask effective, was that I could call on a new Thread process from there, perform network intensive tasks, and finish the thread on the AsyncTask, essentially leaving the main UI doing small work (like displaying the ProgressBar), while the AsyncTask and Runnable Thread did the heavy work.

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main_activity);
	
	Button processBtn = (Button) findViewById(R.id.processBtn);
	OnClickListener listener = new OnClickListener() {
		public void onClick(View view) {
			
			//display the progressbar, from the main UI thread
			progressBar.visibility(View.VISIBLE);
			
			switch (view.getId()) {
				case R.id.processBtn:
					
					//Run the AsyncTask when the button is clicked
					new DoProgressBarWhileProcessing().execute();
					break;
			}
		}
	};
	
	//listen for the onClick 
	processBtn.setOnClickListener(listener);
}

public void SendToNextScreen(View v) { 

    //when the button is clicked, this logic executes 
    Intent nextActivity = new Intent(this, ResultActivity.class); 

    //this method will start on a separate Thread 
    String fullResourceData = ParseUrlResourceNewThread(); 

    //this will now execute on the running asynctask
    String processedData = ProcessRawDataAsync(); 

    nextActivity.putExtra("Key", processedData); 

    startActivity(nextActivity); 
}

//AsyncTask class
public class DoProgressBarWhileProcessing extends AsyncTask<Integer, Integer, String> {

	@Override
	protected String doInBackground(Integer... params)
	{
		
		//call on the method that starts a new thread
		SendToNextScreen(getCurrentFocus());
		
		return null;
	}
	@Override
	protected void onPostExecute(String result) {
		if(progressBar != null)
			progressBar.setVisibility(View.GONE);

	}
	@Override
	protected void onPreExecute() {
		
	}
	@Override
	protected void onProgressUpdate(Integer... values) {
		
	}
	
}

//Runnable thread class
public class ParseUrlThread implements Runnable {
	public ParseUrlThread() {
		
	}
	
	@Override
	public void run() {
		
		//do network tasks here
		
	}
}

I first setup an OnClickListener for the button click, in the onCreate() method of the Activity. From there, I called on the AsyncTask to execute, which called on SendToNextScreen(), which in turn executed the logic within such as launching a new Thread, and processing raw data. The performance, and user experience was much smoother, and faster as well.

Among other things…TransactionTooLarge Exception

Going from one Activity to another on Android, I was also getting a lot of TransactionTooLargeException crash reports on the Developer Console. This was by far a more obscure bug to research, as tracing it did not clearly show the offending line of code, and the results of my search yielded different results.

See here: https://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception

After narrowing down my results, and asking further questions, I was able to see that the crashes were in the name of the exception itself. When I was starting a new Activity, I was passing a large amount of data in the Intent extras Bundle. In this case it was a large String.

You can read more here about ways to fix it: https://stackoverflow.com/questions/12819617/issue-passing-large-data-to-second-activity

 

To read more about Android Threading…

View at Medium.com

 

Hope this was helpful in any way, and thanks for reading 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s