これが私のアプリケーションの基本的なライフサイクルです。デバイスでAndroid2.3.3を実行しているため、SDKバージョン8をターゲットにしています。
- アプリケーションが起動し、
onResume()
呼び出されます。
メソッドshow()
は、キャッシュされたデータを表示するために呼び出されます。 - データをダウンロードして保存するバックグラウンドサービスが開始されます。
AsyncTask
インスタンスを使用して作業を実行します。 - タスクの1つは、ダウンロードしたデータを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
でのみ使用する必要があるように聞こえます。Activity
Service