Android AsyncTask未在从通知启动的活动中运行onPostExexute

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加载完整的消息内容时,但仅在某些情况下,才会

我正在编写一个Android应用程序,它使用
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);
    }