AsyncTask未在通知中启动的Activity中运行onPostExexute

时间:2015-03-26 19:42:54

标签: android android-asynctask android-notifications

我正在编写一个Android应用,使用AlarmManagerWakefulBroadcastReceiver在后​​台检查新邮件。接收方启动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>

有几种情况:

  1. 我从启动器启动应用程序 - >的 WORKS 即可。
  2. 应用程序已启动,如(1),发送到后台,然后消息到达。点击通知,活动开始 - >的 WORKS 即可。
  3. 应用程序已启动,如(1),发送到后台,设备重新启动。消息到达,点击通知,活动开始 - >的例外即可。
  4. 问题:我如何解决这个问题,而不是在开发人员中烧掉在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)
    

1 个答案:

答案 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.htmlhttp://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。特别是关于配置更改的部分可能会有所帮助。