安卓:CalledFromWrongThreadException当广播意图处理抛出 [英] Android: CalledFromWrongThreadException thrown when broadcast intent is handled

查看:198
本文介绍了安卓:CalledFromWrongThreadException当广播意图处理抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我的应用程序的基本生命周期。它现在的目标SDK版本8,因为我还在我的设备上运行Android 2.3.3。

Here is the basic life cycle of my application. It targets SDK version 8 by now, since I am still running Android 2.3.3 on my device.


  • 应用程序启动时, onResume()是所谓的结果
    该方法显示()被称为显示缓存的数据。

  • 后台服务得到启动其下载和存储数据。它采用的AsyncTask 实例来完成其工作。

  • 一个任务商店在SQLite数据库中下载的数据。

  • 广播意图在 onPostExecute发送()当存储的任务已经完成。

  • MapActivity 接收的意图和处理它。结果
    该方法显示()被称为显示缓存和新的数据。

  • The application starts, onResume() is called
    The method show() is called to display cached data.
  • A background service gets started which downloads and stores data. It uses AsyncTask instances to accomplish its work.
  • One of the tasks stores downloaded data in a SQLite database.
  • A broadcast intent is sent in onPostExecute() when the storing task has finished.
  • The MapActivity receives the intent and handles it.
    The method show() is called to display cached and new data.

在该方法显示()地图上看得到的无效覆盖已添加之后。这工作得很好,当显示()已从MapActivity本身调用。它的引发了一个异常,然而,当asynchonous任务的方法调用(间接)的源

Within the method show() the map view gets invalidated after the overlay has been added. This works fine when show() has been called from the MapActivity itself. It raises an exception, however, when the asynchonous task is the source of the method call (indirectly).

据我了解,我在这两种情况下的 UI线程当我触发显示()。这是真的吗?

As far as I understand, I am at the UI thread when I trigger show() in both cases. Is this true?

    public class CustomMapActivity extends MapChangeActivity {

        private boolean showIsActive = false;

        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals(IntentActions.FINISHED_STORING)) {
                    onFinishedStoring(intent);
                }
            }
        };

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            registerReceiver(mReceiver, new IntentFilter(IntentActions.FINISHED_STORING));
        }

        @Override
        protected void onResume() {
            super.onResume();
            show();
        }

        @Override
        protected void onMapZoomPan() {
            loadData();
            show();
        }

        @Override
        protected void onMapPan() {
            loadData();
            show();
        }

        @Override
        protected void onMapZoom() {
            loadData();
            show();
        }

        private void onFinishedStoring(Intent intent) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                boolean success = extras.getBoolean(BundleKeys.STORING_STATE);
                if (success) {
                    show();
                }
        }

        private void loadData() {
            // Downloads data in a AsyncTask
            // Stores data in AsyncTask
        }

        private void show() {
            if (showIsActive) {
                return;
            }
            showIsActive = true;
            Uri uri = UriHelper.getUri();
            if (uri == null) {
                showIsActive = false;
                return;
            }
            Cursor cursor = getContentResolver().query(uri, null, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                List<Overlay> mapOverlays = mapView.getOverlays();
                CustomItemizedOverlay overlay = ItemizedOverlayFactory.getCustomizedOverlay(this, cursor);
                if (overlay != null) {
                    mapOverlays.clear();
                    mapOverlays.add(overlay);
                }
            }
            cursor.close();
            mapView.invalidate(); // throws CalledFromWrongThreadException
            showIsActive = false;
        }

    }

下面是堆栈跟踪...

Here is the stack trace ...

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRoot.checkThread(ViewRoot.java:3020)
    at android.view.ViewRoot.invalidateChild(ViewRoot.java:647)
    at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:673)
    at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
    at android.view.View.invalidate(View.java:5332)
    at info.metadude.trees.activities.CustomMapActivity.showTrees(CustomMapActivity.java:278)
    at info.metadude.trees.activities.CustomMapActivity.onMapPan(CustomMapActivity.java:126)
    at info.metadude.trees.activities.MapChangeActivity$MapViewChangeListener.onChange(MapChangeActivity.java:50)
    at com.bricolsoftconsulting.mapchange.MyMapView$1.run(MyMapView.java:131)
    at java.util.Timer$TimerImpl.run(Timer.java:284)

请注意:我使用的 MapChange 项目,以便接收地图事件的通知

Note: I use the MapChange project in order to receive notifications on map events.

编辑:

从我现在阅读文档中有关的AsyncTask (向下滚动了一下),我不知道如果我使用它的正确方法。从服务类中为previously提到我开始的AsyncTask 实例。在相反,文档指出...

From what I now read in the documentation about AsyncTask (scroll down a bit), I am not sure if I use it the correct way. As previously mentioned I start AsyncTask instances from within a Service class. In contrary, the documentation states ...

的AsyncTask允许您在用户界面上执行异步工作。它在辅助线程执行阻塞操作,然后发布在UI线程上的结果,而不需要您自己处理线程和/或处理程序。

AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.

......这听起来好像的AsyncTask 只应活动中使用 A <$ C内不$ C>服务?!

... which sounds as if AsyncTask should only be used within an Activity not within a Service?!

推荐答案

原因你的崩溃是因为你正在使用的MapChange库的实现方式。引擎盖下,该库使用定时的TimerTask 的实施推迟发射的变化事件,并减少呼叫数量的应用程序获取到 onMapChanged()。但是,您可以从定时的文档看到它运行其任务创建的线程:

The reason for your crash is because of the way that the MapChange library you are using is implemented. Under the hood, this library uses Timer and TimerTask implementations to delay firing the change event and reduce the number of calls your application gets to onMapChanged(). However, you can see from the docs on Timer that it runs its tasks in created threads:

每个定时器有哪些任务顺序执行一个线程。当这个线程繁忙运行任务,可运行的任务可能会受到延迟。

Each timer has one thread on which tasks are executed sequentially. When this thread is busy running a task, runnable tasks may be subject to delays.

由于MapChange库无助于确保回调发布到你的主线程的应用程序(一个严重的错误IMO,尤其是在Android上),你有C你打电话,因为这听众的结果,以保护$ C $ 。你可以看到的例子本 MyMapActivity 与库捆绑在一起,一切从回调得到通过处理程序哪些职位漏斗呼叫回主线程你。

Since the MapChange library does nothing to ensure that callbacks are posted to your application on the main thread (a serious bug IMO, especially on Android), you have to protect the code you call as a result of this listener. You can see this in the example MyMapActivity bundled with the library, everything from that callback gets funneled through a Handler which posts the calls back to the main thread for you.

在您的应用程序,里面的code onMapPan(),随后 showTrees()被称为在后台线程,所以它不是安全的操作UI那里。使用一个处理程序 runOnUiThread()活动将保证您code被称为在正确的地方。

In your application, the code inside onMapPan() and subsequently showTrees() is being called on a background thread so it is not safe to manipulate the UI there. Using either a Handler or runOnUiThread() from your Activity will guarantee your code is called in the right place.

通过关于的AsyncTask 关于你的第二个问题,有什么使用它里面的任何应用程序组件,而不仅仅是活动。尽管这是一个背景部分,默认情况下服务仍是主线程上运行良好,因此的AsyncTask 仍然有必要暂时卸载长期处理的另一个线程。

With regards to your second questions about AsyncTask, there is nothing stopping you from using it inside of any application component, not just Activity. Even though it's a "background" component, by default a Service is still running on the main thread as well, so AsyncTask is still necessary to offload long-term processing to another thread temporarily.

这篇关于安卓:CalledFromWrongThreadException当广播意图处理抛出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆