我正在编写一个Android应用,使用AlarmManager
和WakefulBroadcastReceiver
在后台检查新邮件。接收方启动IntentService
并使用AsyncTask
作为网络请求(HTTPClient)。
重启后,使用收听android.intent.action.BOOT_COMPLETED
的BroadcastReceiver重新启用闹钟。
当我的主Activity的onCreate方法使用AsyncTask加载完整的消息内容时出现问题,但仅在某些情况下:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
doInBackground
已执行并返回包含有效内容的String
,但不会执行onPostExecute
,而是会增加。{/ p>
有几种情况:
问题:我如何解决这个问题,而不是在开发人员中烧掉在UI线程上进行网络请求的地狱?
在搜索解决方案时,我找到onPostExecute not being called in AsyncTask (Handler runtime exception),但“sdw”和“Jonathan Perlow”的解决方案都没有改变。
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
与lastID类似,但它使用String
填充TextView
。
编辑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);
}
}
编辑2:堆栈跟踪:
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)
答案 0 :(得分:1)
我认为这里提到的AsyncTask错误的解决方法:onPostExecute not being called in AsyncTask (Handler runtime exception)在这种情况下也会起作用,但根据OP他们没有。
我建议使用AsyncTaskLoader而不是修复问题。有很多关于AsyncTaskLoader与AsyncTask相比差异(主要是优点)的文章。这里只有两个(好的)例子): http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html和http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
AsyncTaskLoader的主要优点是它们与启动它的Activity或Fragment的生命周期同步,并且它们可以优雅地处理配置更改(与AsyncTasks不同)。
以下是您如何实施它。首先,您需要AsyncLoaderClass:
class ServerQuery extends AsyncTaskLoader<String> {
String mResult;
public ServerQuery(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (mResult == null) {
forceLoad(); // no data yet -> force load it
}
else {
deliverResult(mResult); // already got the data -> deliver it
}
}
@Override
public String loadInBackground() {
// load your data...
return mResult; // return the loaded data
}
@Override
public void cancelLoadInBackground() {
// do whatever it takes to stop loading
}
}
用doInBackground方法中的任何内容替换“//加载数据...”部分。这就是实际的AsyncTaskLoader。正如你所看到的那样,没有与onPostExecute等效的东西,这就是AsyncTaskLoader的优点,它可以加载数据,没有别的。由于AsyncTask试图更新onPostExecute上的ui而发生的所有问题都消失了。
onPostExecute中的逻辑现在完全驻留在启动加载器的Activity / Fragment中。这是你如何做到的:
LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(0, null, this);
这个代表LoaderManager.LoaderCallbacks,这意味着您必须在片段/活动中实现该接口,如下所示:
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new ServerQuery(this);
}
@Override public void onLoaderReset(Loader<String> loader) {}
@Override
public void onLoadFinished(Loader<String> loader, String data) {
// here goes your code to update the ui (previously onPostExecute);
}
onCreateLoader只是创建你的加载器(本例中是ServerQuery),它将由LoaderManager管理。当数据传递到您的片段/活动时,将调用onLoadFinished,允许您更新ui。您可以查看我的其他答案之一,以获取有关可能有助于您了解其工作方式的装载程序的更多信息:https://stackoverflow.com/a/20916507/534471。特别是关于配置更改的部分可能会有所帮助。