2010.04.03 19:29

[android] MapView와 ItemizedOverlay 사용 시 NullPointerException이 발생하는 경우

Android SDK에는 Open Source를 이용한 API와 함께 Google에서 제공하는 서비스를 이용 할 수 있는 별도의 API가 포함된 SDK가 있다. 이를 이용한 것 중 대표적인 것이 바로 MapView일 것이다.

MapView는 사용자가 만드는 Android Activity에 Google Map을 보여주는 View 클래스이다. 이를 이용하면 Google Map 서버에서 지도와 함께 다양한 정보를 얻을 수 있으므로 꽤나 유용하다. 물론 Google이 직접 만든 Google Map App과는 차이가 있지만 거의 비슷하게나마 사용 할 수 있다.

Google Map App에서 검색을 할 경우 화면에 보여지는 것은 ItemizedOverlay라는 것을 이용한다. 이는 지도 화면 위에 아이템을 투영하여 보여주는 것으로 검색 좌표 결과를 지도와 함께 표시해 줄 때 사용하곤 한다. ItemizedOverlay를 사용 할 때 간혹 지도 화면을 Touch하면 바로 아래와 같은 NullPointerException이 발생하는 경우가 있다.

WARN/dalvikvm(661): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
ERROR/AndroidRuntime(661): Uncaught handler: thread main exiting due to uncaught exception
ERROR/AndroidRuntime(661): java.lang.NullPointerException
ERROR/AndroidRuntime(661):     at com.google.android.maps.ItemizedOverlay.getItemsAtLocation(ItemizedOverlay.java:617)
ERROR/AndroidRuntime(661):     at com.google.android.maps.ItemizedOverlay.getItemAtLocation(ItemizedOverlay.java:586)
ERROR/AndroidRuntime(661):     at com.google.android.maps.ItemizedOverlay.handleMotionEvent(ItemizedOverlay.java:498)
ERROR/AndroidRuntime(661):     at com.google.android.maps.ItemizedOverlay.onTouchEvent(ItemizedOverlay.java:572)
ERROR/AndroidRuntime(661):     at com.google.android.maps.OverlayBundle.onTouchEvent(OverlayBundle.java:63)
ERROR/AndroidRuntime(661):     at com.google.android.maps.MapView.onTouchEvent(MapView.java:625)
ERROR/AndroidRuntime(661):     at android.view.View.dispatchTouchEvent(View.java:3709)
ERROR/AndroidRuntime(661):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:851)
ERROR/AndroidRuntime(661):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:821)
ERROR/AndroidRuntime(661):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:821)
ERROR/AndroidRuntime(661):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:821)
ERROR/AndroidRuntime(661):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:821)

이 Exception은 MapView에 등록한 ItemizedOverlay에 OverlayItem이 하나도 없는 경우 발생하는 경우이다.  Open source가 아닌 Google API에서 발생하는 것으로 명확한 오류는 잘 모르지만 짐작컨데 다음과 같은 원인이 있는 것 같아 다음과 같은 상황을 생각해 볼 수 있다.

1. 화면을 Touch 할 경우만 발생하므로 Touch에 관련된 것이다.
2. ItemizedOverlay를 사용할 경우만 발생하므로 ItemizedOverlay에 대한 정보가 더 필요할 것이다.

즉,  ItemizedOverlay를 좀 더 살펴 볼 필요가 있다. ItemizedOverlay는 MapView에 추가하여 MapView 보다 먼저 Touch 이벤트를 받아 처리하며 일반적인 경우는 ItemizedOverlay에 등록된 Item이 선택이 되었는지 확인하고 선택된 Item의 정보를 callback한다. 이런 점을 미루어 볼 때 Empty item에 대한 처리가 되어 있지 않다는 것을 알 수 있었다.

이와 관련된 정보를 찾다보니 이런 오류가 발생하는 사람들이 꽤 있었는데 ItemizedOverlay를 상속받은 후 Constructor에서 무조건 populate()를 한 번 호출하는 방법으로 오류를 피할 수 있다고 한다.

참고 : http://code.google.com/p/android/issues/detail?id=2035

Google에서 제공하는 MapView 예제에는 다음과 같이 ItemizedOverlay에 OverlayItem을 추가할 때 마다 populate()를 호출하도록 되어 있다.

public void addOverlay(OverlayItem overlay) {

    mOverlays
.add(overlay);

    populate
();

}

Java Document에는 다음과 같이 populate() method에 대한 설명이 되어 있다.

void com.google.android.maps.ItemizedOverlay.populate()

protected final void populate()
Utility method to perform all processing on a new ItemizedOverlay. Subclasses provide Items through the createItem(int) method. The subclass should call this as soon as it has data, before anything else gets called.

번역해 보면 이런 내용이다.

새로운 ItemziedOverlay에 대한 모든 처리를 수행하는 유틸리티 메소드. 상속받은 클래스는 Items들을 createItem(int) 메소드를 통해 제공하다. 이 클래스에서는 데이터를 갖은 후 어떤 것을 호출하기 전에 가능한 한 빨리 이 메소드를 호출 해야만 한다.

ItemizedOverlay에 OverlayItem을 등록하거나 변경되면 그 정보를 ItemizedOverlay가 처리하도록 populate()를 호출해야 하는 것인데, 만약 Overlay 생성 후 Item을 하나도 추가하지 않으면 populate()가 아예 호출되지 않게 된다. 그렇기 때문에 Touch 시 잘못된 정보로 인한 오류가 발생한다.

어찌보면 ItemizedOverlay의 구현에 문제가 있는 것일 수도 있지만 그 내부는 알 수 없으니 이를 회피하기 위해 Constructor에서 아예 한 번은 populate()를 호출하는 방법을 이용해 이 오류를 회피하면 Touch 시에도 오류가 발생하지 않게 된다.
Trackback 0 Comment 5