0

I am currently trying to figure out how to avoid a stall in my main thread when exiting ASyncTasks, and I'll try to explain with skeleton code where the problem is specifically. I'm sorry if this is a duplicate, I wasn't seeing anything other than posts about ASyncTasks stalling the main thread during execution. I have a hypothesis of what's causing the problem, but I have no idea if it's right. If it is right, or if it's wrong, I'd like to know the "proper" way to handle the situation.

public class AST extends ASyncTask<String, Integer, Void> {
  private Activity mActivity;
  private interfaceObject mInterface;

  public AST(Activity activity, interfaceObject interface) {
    mActivity = activity;
    mInterface = interface;
  }

  @Override
  protected void onPreExecute() {
    //mInterface.setStatus("Starting...");
    mInterface.setStatus("Doing Stuff...");
  }

  @Override
  protected String doInBackground(Void... params) {
    String string = doStuff();

    //mInterface.setStatus("Did Stuff."); As mentioned this shouldn't be done.

    return string;
  }

  @Override
  protected void onProgressUpdate(Integer... progress) {
    //mInterface.setStatus("Doing Stuff...");
    mInterface.progressUpdate(progress[0], progress[1]);
  }

  @Override
  protected void onPostExecute(String result) {
    mInterface.setStatus("Finished.");
    mInterface.astDone(result);
  }

  private String doStuff() {
    StringBuilder string = "";
    for(int i=0; i<10000; i++) {
      string.append("A");
      publishProgress(i, 10000);
    }
    return string.toString();
  }
}

For specifics, setStatus(String status) edits the text of a textView on the main thread. progressUpdate(int progress, int max) does the same thing for a progressbar. At around 90% the progress bar freezes with the whole UI (despite a Log.d outputting the correct values). The message "Did Stuff." never appears, and there are ~200 skipped frames that get larger as the string gets larger. After the 200 skipped frames, "Finished." appears and astDone gets called (which starts another ASyncTask).

Now why would this hang?

My theory is that when the main thread has to read in the large string, it's stalling, but I don't know if I'm way off base on that? Is there a "proper" way to pass large data from one ASyncTask to another? I suppose I could write to a file in one and read from it in the other, but is that what's considered proper?

Thank you for your time!

Also, just as an aside question, if I slow down the doStuff() method, by adding in an additional loop, the progress bar will make it to 100% and "Finished." does appear. Shouldn't the main thread always receive these before stalling on my large string? Does the main thread not have an order handler, so it is ideal for me to make my publishProgress calls take as long or longer than the main thread takes to actually display the progress? I just don't understand how the main thread can stall at different points in my ASyncTask's calls to the main thread. Shouldn't the main thread execute the calls from ASyncTask as they are received, then hit the variable read and stall?

Edit1: I have changed the code to match Melquiades's suggestions, unfortunately I'm still skipping 200 frames.

Edit2: Since it's coming up I'll include the code for setStatus and progressUpdate:

@Override
public void progressUpdate(int cur, int max) {
    progressBar.setMax(max);
    progressBar.setProgress(cur);
}

@Override
public void setStatus(String status) {
    statusBar.setText(status);
}

progressBar and statusBar are both member variables that are initialized onCreate(), although looking now there's probably no reason they need to be. Is this wrong?

private ProgressBar progressBar;
private TextView statusBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    statusBar = (TextView) findViewById(R.id.textView);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
}

Edit3: Moved setStatus() out of onProgressUpdate() as suggested.

Ben
  • 11
  • 1
  • 2

1 Answers1

0

setStatus(String status) edits the text of a textView on the main thread.

How do you do that exactly (what's the body if this method?) Keep in mind that in AsyncTask, one should not attempt accessing UI elements from doInBackground() - this methods does not run on UI thread, as opposed to 3 other callbacks (onPreExecute, onPublishProgress, and onPostExecute).

Another thing - this innocent line:

string += "A"; 

actually creates new string 10k times as Strings are immutable.

Things to try:

  • see what happens when you comment out setStatus call in doInBackground()
  • use StringBuilder instead of String in your doStuff() method
Community
  • 1
  • 1
Melquiades
  • 8,496
  • 1
  • 31
  • 46
  • The setStatus method is on an interface on the main thread. You're right I probably shouldn't be doing that (I had forgotten) but had initially added it purely for trying to debug what was going on. I appreciate the reminder. Also, thank you for the information about the StringBuilder, I will try that! – Ben Apr 01 '17 at 22:15
  • It's not letting me edit my comment, I realize what you mean now about the setStatus method, it should not be able to touch those views at all. Why is this not crashing (I *was* editing the view from the doInBackground)? – Ben Apr 01 '17 at 22:22
  • Switching to a StringBuilder properly caused the thread crash when trying to edit views from the background thread, however the frame skips still occur. I have edited the OP accordingly. Thank you! – Ben Apr 01 '17 at 22:33
  • Perhaps it's calling `mInterface.setStatus("Doing Stuff...");`, which in turn sets text on TextView 10k times (or `mInterface.progressUpdate` - not sure what it does) – Melquiades Apr 01 '17 at 22:44
  • I've included setStatus and progressUpdate in the OP. Thanks for your time on this! Which actually probably is setting "Doing Stuff..." 10,000 times, so that's a little silly on my end, I'll change that, but it shouldn't cause a huge stall should it? Edit: I moved the setStatus out of the onProgressUpdate(), I'll edit the OP to match that. Still getting stall. – Ben Apr 01 '17 at 23:01
  • I don't see anything in the above that would cause the stall. Could you host your whole project somewhere on github/bitbucket? – Melquiades Apr 03 '17 at 07:33