Android AsyncTask未在从通知启动的活动中运行onPostExexute
我正在编写一个Android应用程序,它使用Android AsyncTask未在从通知启动的活动中运行onPostExexute,android,android-asynctask,android-notifications,Android,Android Asynctask,Android Notifications,我正在编写一个Android应用程序,它使用AlarmManager和WakefulBroadcastReceiver在后台检查新消息。接收器启动IntentService,并对网络请求(HTTPClient)使用AsyncTask 重新启动后,使用BroadcastReceiver监听android.intent.action.BOOT\u COMPLETED重新启用警报 当my main Activity的onCreate方法使用AsyncTask加载完整的消息内容时,但仅在某些情况下,才会
AlarmManager
和WakefulBroadcastReceiver
在后台检查新消息。接收器启动IntentService
,并对网络请求(HTTPClient)使用AsyncTask
重新启动后,使用BroadcastReceiver监听android.intent.action.BOOT\u COMPLETED重新启用警报
当my main Activity的onCreate方法使用AsyncTask加载完整的消息内容时,但仅在某些情况下,才会出现此问题:
java.lang.RuntimeException: Handler (android.os.AsyncTask$InternalHandler) {4139f618} sending message to a Handler on a dead thread
执行doInBackground
并返回一个包含有效内容的字符串
,但不是在postExecute上执行
有几种情况:
我从启动器启动应用程序->工作
该应用程序已像(1)一样启动,发送到后台,然后收到一条消息。点击通知,活动开始->工作
应用程序已像(1)一样启动,发送到后台,设备已重新启动。消息到达,点击通知,活动开始->异常
问题:我如何解决这个问题,而不必为在UI线程上执行网络请求而在开发人员地狱中燃烧
我找到了一个解决方案,但无论是“sdw”还是“Jonathan Perlow”的解决方案都没有改变任何事情
服务器查询:
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();
}
}
Angebot
类似于lastID,但它使用字符串来填充TextView
s。
编辑1:
Angebot:
我假设这里提到的AsyncTask bug的解决方法:在这种情况下也会起作用,但根据OP,它们不起作用
我建议使用AsyncTaskLoader,而不是解决这个问题。关于AsyncTaskLoader与AsyncTask的区别(主要是优势)的文章很多。以下是两个(好的)例子:
及
AsyncTaskLoader的主要优点是,它们与启动它的活动或片段的生命周期同步,并且可以优雅地处理配置更改(与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
}
}
此代表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(int-id,Bundle-args){
返回新的ServerQuery(this);
}
@重写公共void onLoaderReset(加载程序){}
@凌驾
public void onLoadFinished(加载器,字符串数据){
//下面是更新ui的代码(以前是onPostExecute);
}
onCreateLoader只创建加载程序(本例中为ServerQuery),此后将由LoaderManager管理。当数据被传递到允许您更新ui的片段/活动时,将调用onLoadFinished。您可以查看我的另一个答案,以获得有关加载程序的更多信息,这些信息可能有助于您了解加载程序的工作原理:。特别是关于配置更改的部分可能会有所帮助。我怀疑您是否能得到问题的答案,因为您的目标还不清楚。IntentService与活动有什么关系?哪个组件正在启动哪个异步任务?什么是Angebot?它的定义在哪里?为什么要在IntentService(已经在自己的线程中运行)中启动异步任务?发生了什么异常(堆栈跟踪)?如果不把事情弄清楚,没有人会花时间去弄清楚你的问题是什么以及如何解决它。我已经添加了“Angebot”类。IntentService中也使用了AsyncTask(ServerQuery),因为我不想复制doInBackground代码。对于IntentService中的特殊用例,我自己调用doInBackground和onPostExecute,因为正如您所述,它在自己的线程中运行。马上堆叠跟踪。如何从通知开始活动以及清单(活动部分)的外观如何?我认为您可以尝试修复此问题,或者改用AsyncTaskLoader。我猜AsyncTask是从通知线程启动的,当通知消失时,通知线程就会消失。上面提到的帖子中的补丁应该可以在这里使用。无论如何,也许使用加载器会有一些好处。这个活动是从类“lastID”开始的,它在类中写着“//Benachrichtigung”。明天我会试试AsyncTaskLoader。。。
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);
}
}
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)
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
}
}
LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(0, null, this);
@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);
}