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