1

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

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

在该方法show()中,地图视图在添加叠加层后失效。show()当从 MapActivity 本身调用时,这可以正常工作。但是,当异步任务是方法调用的来源(间接)时,它会引发异常。

据我了解,当我在这两种情况下触发时,我都在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只能在 a 内使用,Activity而不是在 a 内使用Service?!

4

2 回答 2

1

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

runOnUiThread(new Runnable() {
    public void run() {
        mapView.invalidate();
    }});
于 2012-07-12T22:23:49.103 回答
1

崩溃的原因是您使用的 MapChange 库的实现方式。在幕后,这个库使用TimerTimerTask实现来延迟触发更改事件并减少应用程序调用的次数onMapChanged()。但是,您可以从文档中Timer看到它在创建的线程中运行其任务:

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

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

在您的应用程序中,内部onMapPan()和随后的代码在showTrees()后台线程上被调用,因此在那里操作 UI 是不安全的。使用 aHandlerrunOnUiThread()from your Activitywill 保证您的代码在正确的位置被调用。

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

于 2012-07-16T16:54:05.863 回答