Reputation: 179
So I've got these long running server calls over what is basically OData going on in my Android application. The consumer of the calls uses .execute().get()
to wait for the response from the network thread (I know the proper way is to make the whole thing asynchronous and callback-based, but the app cannot function in any way without this data, and completely rearchitecting it to work in that way doesn't seem to provide any benefits).
So I found many snippets online where using the ProgressDialog
in combination with onPreExecute()
and onPostExecute()
as shown in the code sample below can be used to show a progress dialog while the AsyncTask
executes. I'm using exactly the samples provided, but what happens is that the call starts, it waits for the network transaction, then very quickly flashes and hides the progress dialog. It can sit for whole seconds waiting on the transaction and I know for a fact that it's waiting in the doInBackground()
, but the dialog just won't pop up until the very end, making it effectively useless.
In the code below the DoEvents()
bit is basically just a very short sleep. I've tried with and without it and there doesn't seem to be a difference, but it seemed worth trying.
class GetFromServerTask extends AsyncTask<String, Void, String>
{
private Context context;
private ProgressDialog dialog;
public GetFromServerTask(Context ctx) {
context = ctx;
dialog = new ProgressDialog(ctx);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog.setMessage("Loading...");
dialog.show();
DoEvents();
}
@Override
protected String doInBackground(String... parms) {
if(InOfflineMode)
return "notdeserializable";
String url = parms[0];
HttpURLConnection urlConnection = null;
try {
URL typedUrl = new URL(url);
urlConnection = (HttpURLConnection) typedUrl.openConnection();
//Add Authorization token
if(InDebugMode) {
urlConnection.addRequestProperty("AuthToken", AuthToken);
} else {
urlConnection.addRequestProperty("Authorization", "Bearer " + AuthToken);
}
urlConnection.addRequestProperty("Accept", "application/json");
DoEvents();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
byte[] contents = new byte[in.available()];
int bytesRead = 0;
String strContents = "";
while((bytesRead = in.read(contents)) != -1){
strContents += new String(contents, 0, bytesRead);
DoEvents();
}
if(strContents.startsWith("<HTML>"))
return "Error: Received unexpected HTML when connecting to service. Make sure you are not connected to a WIFI that requires authentication.";
return strContents;
} catch(UnknownHostException hex) {
return "Error: Could not find server address. Make sure you are connected to the internet. If you just changed connections (ie: turning WIFI on or off) it make take a minute to refresh";
}
catch(Exception ex) {
String msg = "Error: " + ex.getClass().getName() + ": " + ex.getMessage();
Log.e("TE", msg);
return msg;
} finally {
if(urlConnection != null)
urlConnection.disconnect();
}
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(dialog != null && dialog.isShowing())
dialog.dismiss();
DoEvents();
}
}
I've also tried the slightly different version suggested elsewhere on SO (shown below) with the same exact results:
protected void onPreExecute() {
dialog=ProgressDialog.show(context, "", "Loading...", true, false);
super.onPreExecute();
}
I've also tried taking the ProgressDialog
out of the AsyncTask
all together and showing it "outside" the task, as shown below. In this case it doesn't even appear.
ProgressDialog dialog = ProgressDialog.show(ServerAccessLayer.m_context, "", "Loading...", true, false);
String retVal = new GetFromServerTask(ServerAccessLayer.m_context).execute(url).get();
dialog.dismiss();
return retVal;
Upvotes: 2
Views: 2400
Reputation: 6094
As @Gabe-Sechan says, the .get()
is the reason why your UI freezes and your ProgressDialog doesn't show as you want. On the other hand, I disagree with him when he says:
If you want to show the dialog, you need to rearchitect your app to actually use the task asynchronously.
If you simply remove your .get()
and leave you dialog showing in the onPreExecute()
and dismissing in the onPostExecute(String result)
, you'll get what you are looking for: a ProgressDialog shown while the task is executed.
UPDATE:
I started a question based on the sense/usefulness of using the get()
method of Android's AsyncTask class.
Upvotes: 1
Reputation: 93569
Ok, your problem is your .get()
. .get
is a blocking call. This means you won't return to the event loop (the code in the Android Framework that calls onCreate
, onPause
, event handlers, onPostExecute
, onPreExecute
, etc) until after it returns. If you don't return to the event loop, you won't ever go into the drawing code, and you won't display the progress dialog. If you want to show the dialog, you need to rearchitect your app to actually use the task asynchronously. Side note- if you're calling .get()
like that on your UI thread, your entire app will freeze up and look broken. That's why they forced people to not do network IO on the UI thread in the first place.
Upvotes: 3