安卓:CalledFromWrongThreadException当广播意图处理抛出(Android

2019-10-16 23:45发布

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

  • 应用程序启动后onResume()被调用
    该方法show()被调用以显示高速缓存的数据。
  • 后台服务得到启动,其下载和存储数据。 它使用AsyncTask实例来完成其工作。
  • 其中一个任务商店在SQLite数据库下载数据。
  • 广播意图在发送onPostExecute()当存储的任务已经完成。
  • MapActivity接收的意图和处理它。
    该方法show()被调用以显示缓存和新的数据。

在这个方法中show()覆盖已经加入后,地图视图获取无效 。 当这工作正常show()已经从MapActivity本身调用。 它引发了异常 ,然而,当asynchonous任务是方法调用(间接地)的源极。

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

    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;
        }

    }

这里是堆栈跟踪...

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项目,以在地图上的事件接收通知。


编辑:

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

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

......这听起来仿佛AsyncTask应该只在内部使用Activity不是内Service

Answer 1:

原因你的崩溃是因为你使用的是MapChange库的实现方式。 引擎盖下,这个库使用TimerTimerTask实现推迟发射的变化事件,并减少应用程序获取到的呼叫数量onMapChanged() 但是,你可以从上Docs看到Timer ,它运行的任务创建的线程:

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

由于MapChange库没有采取任何措施确保回调发布到你的主线程应用(一个严重的错误IMO,尤其是在Android上),你要保护你打电话,因为这监听的结果的代码。 你可以看到的例子这MyMapActivity与库捆绑在一起,一切都来自回调获得通过一个漏斗Handler哪些职位呼叫回你的主线程。

在您的应用程序,里面的代码onMapPan()随后showTrees()被调用在后台线程,所以它不是安全的操作UI那里。 无论是使用一个HandlerrunOnUiThread()从你的Activity将保证你的代码被称为在正确的地方。

至于你的第二个有关问题AsyncTask ,没有什么使用它的任何应用程序组件,而不仅仅是内部阻止你Activity 。 尽管这是一个“背景”部分,默认情况下, Service仍是主线程上运行一样,所以AsyncTask仍然需要暂时卸载长期加工到另一个线程。



Answer 2:

如果它变得叫错线程上,那么它可能不会在UI线程上。 你有没有试过这样:

runOnUiThread(new Runnable() {
    public void run() {
        mapView.invalidate();
    }});


文章来源: Android: CalledFromWrongThreadException thrown when broadcast intent is handled