我正在编写一个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,则会引起说明。

有几种方案:


我从启动器-> 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.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中的逻辑完全位于启动加载程序的活动/片段中。这是您的操作方式:

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/

10-10 02:05