我正在编写一个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
,则会引起说明。有几种方案:
我从启动器-> WORKS启动该应用程序。
该应用程序已像(1)一样启动,发送到后台,然后出现一条消息。点击通知,活动开始->工作。
该应用程序已像(1)一样启动,发送到后台并且设备已重启。一条消息到达,点击通知,活动开始-> EXCEPTION。
问题:如何解决此问题,而又不打扰开发人员在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)
最佳答案
我认为这里提到的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中的逻辑完全位于启动加载程序的活动/片段中。这是您的操作方式:
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。特别是有关配置更改的部分可能会有所帮助。
关于android - 从通知启动的 Activity 中,AsyncTask没有在onPostExexute上运行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29287451/