【问题标题】:Multiple notifyObservers() on async task not working异步任务上的多个 notifyObservers() 不起作用
【发布时间】:2017-05-10 20:07:14
【问题描述】:

我使用 AsyncTask 通过 Internet 获取和上传数据。我想在活动上显示任务的当前状态。为此,我使用了观察者,它将当前状态发送到我的活动。所以这是我的Background.java 代码的简短 sn-p:

private class Syncing extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... params) {
            sync.init();
            if (Sync.isOnline()) {
                setChanged();
                notifyObservers(ObserverStatus.UPLOAD_PENDING);
                sync.upload(); //Upload data
                setChanged();
                notifyObservers(ObserverStatus.DOWNLOAD_PENDING);
                sync.download(); //Download Data
                setChanged();
                notifyObservers(ObserverStatus.SYNC_COMPLETED);
            } else {
                setChanged();
                notifyObservers(ObserverStatus.OFFLINE);
            }
            return "Executed";
        }

        @Override
        protected void onPostExecute(String result) {
            setChanged();
            notifyObservers(ObserverStatus.FINISHED);
        }

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected void onProgressUpdate(Void... values) {
        }
    }

这个Syncing 类嵌入在Background 类中。在我使用第二个notifyObservers() 调用之前,这段代码没有任何问题。所以在这种情况下,如果用户在线并启动任务,第一个notifyObservers()会被调用,但第二个,第三个,......不会被调用,它会抛出以下错误:

05-10 21:50:21.563 16265-16285/net.myapp.example E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: net.myapp.example, PID: 16265
java.lang.RuntimeException: An error occurred while executing doInBackground()
     at android.os.AsyncTask$3.done(AsyncTask.java:325)
     at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
     at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
     at java.util.concurrent.FutureTask.run(FutureTask.java:242)
     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
     at java.lang.Thread.run(Thread.java:761)
 Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6892)
     at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1083)
     at android.view.ViewGroup.invalidateChild(ViewGroup.java:5205)
     at android.view.View.invalidateInternal(View.java:13656)
     at android.view.View.invalidate(View.java:13620)
     at android.view.View.invalidate(View.java:13604)
     at android.widget.ImageView.setImageDrawable(ImageView.java:531)
     at net.myapp.example.MainActivity.update(MainActivity.java:239)
     at java.util.Observable.notifyObservers(Observable.java:161)
     at net.myapp.example.sync.Background$Syncing.doInBackground(Background.java:37)
     at net.myapp.example.sync.Background$Syncing.doInBackground(Background.java:27)
     at android.os.AsyncTask$2.call(AsyncTask.java:305)
     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
     at java.lang.Thread.run(Thread.java:761) 

在我的活动的第 238 行,将替换一个图标,您也可以在错误日志中看到。但它会发生在第 37 行。这是第二个 notifyObservers() 将调用的地方。

我该如何解决这个问题?

【问题讨论】:

    标签: java android multithreading asynchronous android-asynctask


    【解决方案1】:

    我现在找到了解决此问题的一种方法。我现在使用onProgressUpdate。所以我添加了以下功能:

    @Override
    protected void onProgressUpdate(ObserverStatus... values) {
        super.onProgressUpdate(values);
        setChanged();
        notifyObservers(values[0]);
    }
    

    现在我可以通过publishProgress() 方法发送进度。幸运的是它工作得很好。

    @Override
    protected String doInBackground(String... params) {
        sync.init();
        if (Sync.isOnline()) {
            publishProgress(ObserverStatus.UPLOAD_PENDING);
            sync.upload(); //Upload data
            publishProgress(ObserverStatus.DOWNLOAD_PENDING);
            sync.download(); //Download Data
            publishProgress(ObserverStatus.SYNC_COMPLETED);
        } else {
            setChanged();
            notifyObservers(ObserverStatus.OFFLINE);
        }
        return "Executed";
    }
    

    感谢您的回答@jereksel。

    【讨论】:

    • @Yazan 感谢您的评论,但我必须等待两天才能接受我自己的回答。
    【解决方案2】:

    Android 视图相关属性只能在 UI 线程中更改。 doInBackground 在无法执行此操作的后台线程上调用。要解决此问题,您应该将 notifyObservers 内容包装在 runOnUiThread (docs) 函数中。

    【讨论】:

    • 感谢您的回答。我已经尝试将其包装起来,但不幸的是异步任务不支持 activity 并且也不支持 runOnUIThread
    • 但同步似乎是私有的非静态类。 Asynctask 本身没有runOnUIThread,但在您的情况下,同步应该可以访问父类。无论如何,您也可以在构造函数中传递 Activity。但是,为了防止内存泄漏,请考虑使用 WeakReference: developer.android.com/reference/java/lang/ref/…
    猜你喜欢
    • 1970-01-01
    • 2012-05-18
    • 2015-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    • 2017-03-29
    相关资源
    最近更新 更多