安卓:CalledFromWrongThreadException当广播意图处理抛出 [英] Android: CalledFromWrongThreadException thrown when broadcast intent is handled
问题描述
下面是我的应用程序的基本生命周期。它现在的目标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 methodshow()
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 methodshow()
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
关于你的第二个问题,有什么使用它里面的任何应用程序组件,而不仅仅是活动停止你code>。尽管这是一个背景部分,默认情况下
服务
仍是主线程上运行良好,因此的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屋!