Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/188.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android:处理广播意图时引发CalledFromErrorThreadException_Android_Broadcastreceiver_Invalidation_Mapactivity - Fatal编程技术网

Android:处理广播意图时引发CalledFromErrorThreadException

Android:处理广播意图时引发CalledFromErrorThreadException,android,broadcastreceiver,invalidation,mapactivity,Android,Broadcastreceiver,Invalidation,Mapactivity,这是我的应用程序的基本生命周期。现在它的目标是SDK版本8,因为我仍然在我的设备上运行Android 2.3.3 应用程序启动时,onResume()被调用 调用方法show()以显示缓存的数据 启动一个下载和存储数据的后台服务。它使用AsyncTask实例来完成其工作 其中一个任务将下载的数据存储在SQLite数据库中 存储任务完成后,将在onPostExecute()中发送广播意图 MapActivity接收并处理意图。 调用方法show(),以显示缓存的数据和新数据 在方法show()

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

  • 应用程序启动时,
    onResume()
    被调用
    调用方法
    show()
    以显示缓存的数据
  • 启动一个下载和存储数据的后台服务。它使用
    AsyncTask
    实例来完成其工作
  • 其中一个任务将下载的数据存储在SQLite数据库中
  • 存储任务完成后,将在
    onPostExecute()
    中发送广播意图
  • MapActivity
    接收并处理意图。
    调用方法
    show()
    ,以显示缓存的数据和新数据
在方法
show()。如果已从MapActivity本身调用了
show()
,则此功能可以正常工作。但是,当异步任务是方法调用的源时(间接),它会引发异常

据我所知,在这两种情况下,当我触发
show()
时,我都处于UI线程中。这是真的吗

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

    }
注意:我使用该项目是为了接收有关映射事件的通知


编辑:

从我现在读到的(向下滚动一点),我不确定我是否正确使用它。如前所述,我从
服务
类中启动
异步任务
实例。相反,文件指出

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


。。。听起来好像
AsyncTask
只能在
活动中使用,而不能在
服务中使用

如果它在错误的线程上被调用,那么它可能不在UI线程上。你试过这个吗:

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

崩溃的原因是您使用的MapChange库的实现方式。在引擎盖下,此库使用
定时器
定时器任务
实现来延迟触发更改事件,并减少应用程序对
onmachanged()
的调用次数。但是,您可以从
Timer
上的文档中看到,它在创建的线程中运行其任务:

每个计时器都有一个线程,在该线程上按顺序执行任务。当此线程忙于运行任务时,可运行任务可能会延迟

由于MapChange库无法确保回调在主线程上发布到应用程序(在我看来,这是一个严重的错误,尤其是在Android上),因此您必须保护由于此侦听器而调用的代码。您可以在与库捆绑在一起的示例
MyMapActivity
中看到这一点,从该回调的所有内容都通过
Handler
传递,该处理程序为您将调用发回主线程

在应用程序中,正在后台线程上调用
onmapan()
中的代码,随后调用
showTrees()
,因此在那里操作UI是不安全的。从
活动中使用
处理程序
runOnUiThread()
将确保在正确的位置调用代码


关于
AsyncTask
的第二个问题,没有什么可以阻止您在任何应用程序组件中使用它,不仅仅是
活动
。即使它是一个“后台”组件,默认情况下,
服务
也仍然在主线程上运行,因此仍然需要
AsyncTask
将长期处理暂时卸载到另一个线程。

您可以说99%的时间是在主线程上调用的
onReceive()
,但这1%取决于它是如何注册的。你能显示代码的
registerReceiver()
部分吗?@Devunwired我添加了
onCreate()
方法来显示
registerReceiver()
。我仍然有兴趣帮你弄清楚为什么会发生这种情况。您可以发布来自错误线程异常的堆栈跟踪吗?这将有助于了解错误呼叫的来源。@Devunwired对延迟表示抱歉。我添加了stacktrace。因此,请仔细查看该堆栈跟踪,注意此调用与您的
广播接收器
无关。您的代码是从
计时器执行的,该计时器在该库中的各个线程上执行任务。该库直接从后台线程调用侦听器,而不是像Android框架那样在主线程上发布回调。它可以工作,但是,我不明白它当时运行的线程是什么。你能解释一下为什么这是必要的吗?我是否有设计错误?可能与您的异步任务有关。看一下关于后台线程计算的部分?由于mapView是由UI线程创建的视图,因此只能在UI线程上修改它。如果在后台线程上调用修改它,它将抛出一个错误。这可能是一种不好的做法,但如果有可能在后台线程上调用编辑视图的方法,请调用runOnUiThread函数以确保它正在UI线程上运行。该任务不应与活动直接连接。我在后台处理完成后发送广播意图。然后,活动处理意图。我在这里发现了一个小贴士:“用意图启动活动是一个前台操作,它修改用户当前与之交互的内容;广播意图是一个用户通常不知道的后台操作。”如果我正确地解释了它,它将在后台运行
runOnUiThread(new Runnable() {
    public void run() {
        mapView.invalidate();
    }});