I am writing an Android App that checks for new messages in the background using AlarmManager and WakefulBroadcastReceiver. The Receiver starts an IntentService and that uses an AsyncTask for the network request (HTTPClient).
After a reboot, the alarm is re-enabled using a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED.
The problem occures when the onCreate method of my main Activity loads the full message contents using an AsyncTask, but only under certain circumstances:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
The doInBackground is executed and returns a String with valid content, but instead of executing onPostExecute, the excaption rises.
There are several scenarios:
- I start the app from the launcher -> WORKS.
- The App has been started like (1), sent to background and then a message arrives. The Notification is tapped, the Activity starts -> WORKS.
- The App has been started like (1), send to background and the device was rebooted. A message arrives, the notification is tapped, the Activity starts -> EXCEPTION.
Question: How can I fix that, without burning in the developer hell for doing network requests on the UI thread?
Searching for a solution, I found onPostExecute not being called in AsyncTask (Handler runtime exception), but neither the solution from "sdw" nor from "Jonathan Perlow" changes a thing.
ServerQuery:
public abstract class ServerQuery extends AsyncTask<Void, Void, String> {
protected Context context;
String user = "ERR";
String pin = "ERR";
String cmd = "ERR";
ServerQuery(Context context, String _user, String _pin, String _cmd) {
this.context = context;
user = _user;
pin = _pin;
cmd = _cmd;
}
private ServerQuery() {
}
@Override
protected String doInBackground(Void... params) {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_client), context.getString(R.string.post_client_app)));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_user), user));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_pin), pin));
nameValuePairs.add(new BasicNameValuePair(context.getString(R.string.post_cmd), cmd));
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://[my server hostname]/index.php");
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
} catch (UnsupportedEncodingException e) {
return "ERR";
}
HttpResponse httpResponse = null;
try{
httpResponse = httpClient.execute(httpPost);
}catch (Exception e) {
return "ERR";
}
HttpEntity httpEntity = httpResponse.getEntity();
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
} catch (IOException e) {
return "ERR";
}
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String s = null;
try {
while ((s = bufferedReader.readLine()) != null) {
stringBuilder.append(s);
}
} catch (IOException e) {
return "ERR";
}
try {
inputStream.close();
} catch (IOException e) {
return "ERR";
}
return stringBuilder.toString();
}
}
lastID:
public class lastID extends ServerQuery {
Integer lastid, savedid;
lastID(Context context, String _user, String _pin, String _cmd) {
super(context, _user, _pin, _cmd);
lastid = -1;
}
@Override
protected void onPostExecute(final String success) {
if(success.equals("ERR") || success.length() < 1) {
Toast.makeText(context, context.getString(R.string.server_no_connection), Toast.LENGTH_LONG).show();
} else {
String content[] = success.split("(;)");
if(content.length == 2) {
lastid = Integer.parseInt(content[0]);
SharedPreferences sharedPreferences = context.getSharedPreferences(context.getString(R.string.settings_filename), 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
savedid = sharedPreferences.getInt(context.getString(R.string.post_lastid), -1);
if (lastid > savedid) { // NEUES ANGEBOT!
editor.putInt(context.getString(R.string.post_lastid), lastid);
editor.commit();
// Benachrichtung
NotificationCompat.Builder notific = new NotificationCompat.Builder(context);
notific.setContentTitle(context.getString(R.string.notify_title));
notific.setContentText(content[1]);
notific.setSmallIcon(R.drawable.ic_notify);
notific.setAutoCancel(false);
notific.setPriority(NotificationCompat.PRIORITY_HIGH);
notific.setTicker(content[1]);
notific.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
Intent resultIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
notific.setContentIntent(resultPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(lastid, notific.build());
}
}
}
}
}
MainActivity onCreate:
[ loading shared preferences, setting onclicklisteners ]
angebot = new Angebot(this, getApplicationContext(), user, pin, getString(R.string.post_cmd_viewAngebot));
angebot.execute((Void) null);
Angebot is like lastID, but it uses the String to fill the TextViews.
EDIT 1:
Angebot:
public class Angebot extends ServerQuery {
protected Activity activity;
protected TextView datumView;
// more TextView s
protected Button hungerButton;
Angebot(Activity activity, Context context, String _user, String _pin, String _cmd) {
super(context, _user, _pin, _cmd);
this.activity = activity;
datumView = (TextView) this.activity.findViewById(R.id.datum);
// finding all other TextView s
}
@Override
protected void onPreExecute() {
showProgress(true);
}
@Override
protected void onPostExecute(final String success) {
Log.v(C.TAG_ALARMESSEN, "Angebot.onPostExecute");
showProgress(false);
if(success.equals("ERR") || success.length() < 1) {
Toast.makeText(activity.getApplicationContext(), context.getString(R.string.server_no_connection), Toast.LENGTH_LONG).show();
} else {
String content[] = success.split("(;)");
// put each content string in a TextView
} else {
Toast.makeText(context, context.getString(R.string.err02s), Toast.LENGTH_LONG).show(); // nicht angemeldet
}
}
}
@Override
protected void onCancelled() {
showProgress(false);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void showProgress(final boolean show) {
final View progressView = activity.findViewById(R.id.login_progress);
progressView.setVisibility(show ? View.VISIBLE : View.GONE);
}
}
EDIT 2: stack trace:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:196)
at android.os.Handler.sendMessageAtTime(Handler.java:473)
at android.os.Handler.sendMessageDelayed(Handler.java:446)
at android.os.Handler.sendMessage(Handler.java:383)
at android.os.Message.sendToTarget(Message.java:363)
at android.os.AsyncTask.postResult(AsyncTask.java:300)
at android.os.AsyncTask.access$400(AsyncTask.java:156)
at android.os.AsyncTask$2.call(AsyncTask.java:264)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)