마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

[제발 도와주세요] 공공데이터 API 카카오맵 띄우기

0 추천
카카오 맵에 띄우려고 하는데 왜 안 불러와질까요..???
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MapView mapView = new MapView(this);

        ViewGroup mapViewContainer = (ViewGroup) findViewById(R.id.map_view);
        mapViewContainer.addView(mapView);
        StrictMode.enableDefaults();

        // 줌 레벨 변경
        mapView.setZoomLevel(4, true);

        // 줌 인
        mapView.zoomIn(true);

        // 줌 아웃
        mapView.zoomOut(true);



        boolean initem = false, inStatNm = false, inStatId = false, inChgerId = false, inChgerType = false,  inAddr = false, inLocation = false, inUserTime = false, inMethod = false, inLat = false, inLng = false, inStatUpdDt = false;
        String statNm = null, statId = null, chgerId = null, chgerType = null, addr = null, location = null, userTime = null, method = null;
        String lat = null, lng = null, statUpdDt = null;

        try{
            URL url = new URL("http://apis.data.go.kr/B552584/EvCharger/getChargerInfo?serviceKey=iifF6nWLmR%2BPgzn%2BZKQfpfbSa%2FxrnJe8cRoVMGBOYvaLg0iv2dluN%2BamznkrvFKRPIQHCZkfU4shudRWucbZag%3D%3D&numOfRows=10&pageNo=123"); //검색 URL부분

            XmlPullParserFactory parserCreator = XmlPullParserFactory.newInstance();
            XmlPullParser parser = parserCreator.newPullParser();

            parser.setInput(url.openStream(), null);

            int parserEvent = parser.getEventType();


            while (parserEvent != XmlPullParser.END_DOCUMENT){
                switch(parserEvent){
                    case XmlPullParser.START_TAG://parser가 시작 태그를 만나면 실행
                        if(parser.getName().equals("statNm")){
                            inStatNm = true;
                        }
                        if(parser.getName().equals("statId")){
                            inStatId = true;
                        }
                        if(parser.getName().equals("chgerId")){
                            inChgerId = true;
                        }
                        if(parser.getName().equals("chgerType")){
                            inChgerType = true;
                        }
                        if(parser.getName().equals("addr")){
                            inAddr = true;
                        }
                        if(parser.getName().equals("location")){
                            inLocation = true;
                        }
                        if(parser.getName().equals("userTime")){
                            inUserTime = true;
                        }
                        if(parser.getName().equals("method")){
                            inMethod = true;
                        }
                        if(parser.getName().equals("lat")){
                            inLat = true;
                        }
                        if(parser.getName().equals("lng")){
                            inLng = true;
                        }
                        if(parser.getName().equals("statUpDt")){
                            inStatUpdDt = true;
                        }
                        if(parser.getName().equals("message")){ //message 태그를 만나면 에러 출력
                        }
                        break;

                    case XmlPullParser.TEXT://parser가 내용에 접근했을때
                        if(inStatNm) {
                            statNm = parser.getText();
                            inStatNm = false;
                        }
                        if(inStatId) {
                            statId = parser.getText();
                            inStatId = false;
                        }
                        if(inChgerId) {
                            chgerId = parser.getText();
                            inChgerId = false;
                        }
                        if(inChgerType) {
                            chgerType = parser.getText();
                            inChgerType = false;
                        }
                        if(inAddr) {
                            addr = parser.getText();
                            inAddr = false;
                        }
                        if(inLocation) {
                            location = parser.getText();
                            inLocation = false;
                        }
                        if(inUserTime) {
                            userTime = parser.getText();
                            inUserTime = false;
                        }
                        if(inMethod) {
                            method = parser.getText();
                            inMethod = false;
                        }
                        if(inLat) {
                            lat = parser.getText();
                            inLat = false;
                        }
                        if(inLng) {
                            lng = parser.getText();
                            inLng = false;
                        }
                        if(inStatUpdDt) {
                            statUpdDt = parser.getText();
                            inStatUpdDt = false;
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        if(parser.getName().equals("item")){

                            /*마커 추가*/

                            // 중심점 변경
                            mapView.setMapCenterPoint(MapPoint.mapPointWithGeoCoord(Long.parseLong(lat), Long.parseLong(lng)), true);

                            //마커 찍기
                            MapPoint MARKER_POINT1 = MapPoint.mapPointWithGeoCoord(Long.parseLong(lat), Long.parseLong(lng));
                            // 마커 아이콘 추가하는 함수
                            MapPOIItem marker1 = new MapPOIItem();
                            // 클릭 했을 때 나오는 호출 값
                            marker1.setItemName(addr + "전기차 충전소 여기있음.");

                            // 왜 있는지 잘 모르겠음
                            marker1.setTag(0);

                            // 좌표를 입력받아 현 위치로 출력
                            marker1.setMapPoint(MARKER_POINT1);

                            //  (클릭 전)기본으로 제공하는 BluePin 마커 모양의 색.
                            marker1.setMarkerType(MapPOIItem.MarkerType.BluePin);


                            // (클릭 후) 마커를 클릭했을때, 기본으로 제공하는 RedPin 마커 모양.
                            marker1.setSelectedMarkerType(MapPOIItem.MarkerType.RedPin);


                            // 지도화면 위에 추가되는 아이콘을 추가하기 위한 호출(말풍선 모양)
                            mapView.addPOIItem(marker1);

                            initem = false;

                        }
                        break;
                }
                parserEvent = parser.next();
            }
        } catch(Exception e){

        }//





    }


}

 

이동국 (120 포인트) 님이 2022년 7월 6일 질문

3개의 답변

0 추천

디버깅을 해보시면 아래와 같은 에러가 발생합니다.

android.os.NetworkOnMainThreadException

에러의 원인은 안드로이드에서는 네트위킹등 리소스가 많이 먹은 작업은 화면을 블락시기키기 때문에 메인쓰레드가 아니라 백그라운드 쓰레드에서 처리하게 되어 있습니다. 화면을 업데이트하는게 주임무인 메인쓰레드를 블락시키면 앱이 버벅거리게 됩니다. 쓰레드를 별도로 생성하지 않으면 메인쓰레드에서 처리하게 되어있기 때문에 백그라운드 쓰레드에서 해당작업을 처리해주어야 합니다. 백그라운드 쓰레드에서 처리한 후 작업완료가 되면 콜백을 받아서 처리하게 됩니다. 이걸 asynchronous(비동기) 이라고도 합니다.

코틀린은 기본적으로 Coroutine이 탑재되어 있기 때문에 이런 처리를 하는게 좀 더 간편한데, 자바는 코드가 좀 더 필요합니다.

구현방법에는 여러가지 옵션이 있는데, Volley나 Retrofit같은 라이브러리를 사용하는게 일반적이구요, 간단하게는 백그라운드 쓰레드를 지원하는 클래스를 하나 사용하면 됩니다. 아래의 클래스를 복사해서 님의 프로젝트에 붙여넣으세요.

https://github.com/c0de-wizard/android-liveData-viewModel/blob/master/app/src/main/java/com/thomaskioko/livedatademo/repository/util/AppExecutors.java

 

그리고 gas station데이터를 읽어오는 부분을 loadElectricGasStationLocations 메소드로 옮기세요. 그런 다음 AppExecutors를 생성해서 newtorkIO를 안에서 호출하시면 됩니다.

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

       ...

       loadMapData();
}

private void loadMapData() {
    AppExecutors appExecutors = new AppExecutors();
    appExecutors.networkIO().execute(new Runnable() {
            @Override
            public void run() {
                loadElectricGasStationLocations();
            }
        });
}

private void loadElectricGasStationLocations() {
        boolean initem = false, inStatNm = false, inStatId = false, inChgerId = false, inChgerType = false,  inAddr = false, inLocation = false, inUserTime = false, inMethod = false, inLat = false, inLng = false, inStatUpdDt = false;
        String statNm = null, statId = null, chgerId = null, chgerType = null, addr = null, location = null, userTime = null, method = null;
        String lat = null, lng = null, statUpdDt = null;
 
        try{
            URL url = new URL("http://apis.data.go.kr/B552584/EvCharger/getChargerInfo?serviceKey=iifF6nWLmR%2BPgzn%2BZKQfpfbSa%2FxrnJe8cRoVMGBOYvaLg0iv2dluN%2BamznkrvFKRPIQHCZkfU4shudRWucbZag%3D%3D&numOfRows=10&pageNo=123"); //검색 URL부분

           ...
        } catch(Exception e){
 
        }//
}

}

한가지 주의하실 점은 map에 마커를 표시하는 코드는 다시 메인쓰레드 안에서 이루어져야 하므로 runOnUiThread를 호출하여 그 안에서 실행하셔야 합니다. 아래 링크를 참조하세요.

https://stackoverflow.com/questions/11140285/how-do-we-use-runonuithread-in-android

이런 추가 코드가 귀찮다면, Volley나 Retrofit같은 라이브러리를 사용하시면 됩니다. RxJava까지 사용하면 좀 더 구조화된 코드를 짜실 수 있구요. 그리고 아직은 힘드시겠지만, 이런 네트워크 처리 작업은 액티비티와는 무관하기 때문에, 별도의 클래스를 두어서 재사용성과 테스트를 쉽게 만들어주는게 일반적인 방식입니다. 그리고 XML 파싱하는 부분도 다시 고민해보셔서 에러처리도 추가하시고 (파싱에러, 응답데이터 에러)가독성을 높이는 방향으로 수정하시고 테스트도 추가하시면 좋을 것 같네요.

 

 

spark (227,530 포인트) 님이 2022년 7월 7일 답변
spark님이 2022년 7월 7일 수정
0 추천

Retrofit을 이용한 처리 소스를 올려드릴게요.

app/build.gradle 에 Retrofit과  Xml parser추가

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'

XML reponse용 클래스 추가
 

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name = "header")
public class ResponseHeader {

    @Element(name = "resultCode")
    private String resultCode = "";

    @Element(name = "resultMsg")
    private String resultMsg = "";

    @Element(name = "totalCount")
    private int totalCount = 0;

    @Element(name = "pageNo")
    private int pageNo = 0;

    @Element(name = "numOfRows")
    private int numOfRows = 0;

    public String getResultCode() {
        return resultCode;
    }

    public void setResultCode(String resultCode) {
        this.resultCode = resultCode;
    }

    public String getResultMsg() {
        return resultMsg;
    }

    public void setResultMsg(String resultMsg) {
        this.resultMsg = resultMsg;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getPageNo() {
        return pageNo;
    }

    public void setPageNo(int pageNo) {
        this.pageNo = pageNo;
    }

    public int getNumOfRows() {
        return numOfRows;
    }

    public void setNumOfRows(int numOfRows) {
        this.numOfRows = numOfRows;
    }

    @Override
    public String toString() {
        return "ResponseHeader{" +
                "resultCode='" + resultCode + '\'' +
                ", resultMsg='" + resultMsg + '\'' +
                ", totalCount=" + totalCount +
                ", pageNo=" + pageNo +
                ", numOfRows=" + numOfRows +
                '}';
    }
}
public class ResponseError {
    private final String code;
    private final String message;

    public ResponseError(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "ResponseError{" +
                "code='" + code + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name = "item", strict = false)
public class EvCharger {
    @Element(name = "statNm")
    private String statNm = "";

    @Element(name = "statId")
    private String statId = "";

    @Element(name = "chgerId")
    private String chgerId = "";

    @Element(name = "chgerType")
    private String chgerType = "";

    @Element(name = "addr")
    private String addr = "";

    @Element(name = "location")
    private String location = "";

    @Element(name = "lat")
    private double lat = 0.0;

    @Element(name = "lng")
    private double lng = 0.0;

    @Element(name = "useTime")
    private String useTime = "";

    @Element(name = "busiId")
    private String busiId = "";

    @Element(name = "bnm")
    private String bnm = "";

    @Element(name = "busiNm")
    private String busiNm = "";

    @Element(name = "busiCall")
    private String busiCall = "";

    @Element(name = "stat")
    private String stat = "";

    @Element(name = "statUpdDt")
    private String statUpdDt = "";

    @Element(name = "method")
    private String method = "";

    public String getStatNm() {
        return statNm;
    }

    public void setStatNm(String statNm) {
        this.statNm = statNm;
    }

    public String getStatId() {
        return statId;
    }

    public void setStatId(String statId) {
        this.statId = statId;
    }

    public String getChgerId() {
        return chgerId;
    }

    public void setChgerId(String chgerId) {
        this.chgerId = chgerId;
    }

    public String getChgerType() {
        return chgerType;
    }

    public void setChgerType(String chgerType) {
        this.chgerType = chgerType;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }

    public double getLng() {
        return lng;
    }

    public void setLng(double lng) {
        this.lng = lng;
    }

    public String getUseTime() {
        return useTime;
    }

    public void setUseTime(String useTime) {
        this.useTime = useTime;
    }

    public String getBusiId() {
        return busiId;
    }

    public void setBusiId(String busiId) {
        this.busiId = busiId;
    }

    public String getBnm() {
        return bnm;
    }

    public void setBnm(String bnm) {
        this.bnm = bnm;
    }

    public String getBusiNm() {
        return busiNm;
    }

    public void setBusiNm(String busiNm) {
        this.busiNm = busiNm;
    }

    public String getBusiCall() {
        return busiCall;
    }

    public void setBusiCall(String busiCall) {
        this.busiCall = busiCall;
    }

    public String getStat() {
        return stat;
    }

    public void setStat(String stat) {
        this.stat = stat;
    }

    public String getStatUpdDt() {
        return statUpdDt;
    }

    public void setStatUpdDt(String statUpdDt) {
        this.statUpdDt = statUpdDt;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "Station{" +
                "statNm='" + statNm + '\'' +
                ", statId='" + statId + '\'' +
                ", chgerId='" + chgerId + '\'' +
                ", chgerType='" + chgerType + '\'' +
                ", addr='" + addr + '\'' +
                ", location='" + location + '\'' +
                ", lat=" + lat +
                ", lng=" + lng +
                ", useTime='" + useTime + '\'' +
                ", busiId='" + busiId + '\'' +
                ", bnm='" + bnm + '\'' +
                ", busiNm='" + busiNm + '\'' +
                ", busiCall='" + busiCall + '\'' +
                ", stat='" + stat + '\'' +
                ", statUpdDt='" + statUpdDt + '\'' +
                '}';
    }
}

 

 

spark (227,530 포인트) 님이 2022년 7월 7일 답변
0 추천

Retrofit interface추가

public interface EvChargerApi {

    @GET("B552584/EvCharger/getChargerInfo?serviceKey=iifF6nWLmR%2BPgzn%2BZKQfpfbSa%2FxrnJe8cRoVMGBOYvaLg0iv2dluN%2BamznkrvFKRPIQHCZkfU4shudRWucbZag%3D%3D&numOfRows=10&pageNo=123")
    Call<ChargerInfoResponse> getChargerInfo();
}

 

Retrofit을 Singleton으로 처리하기위한 클래스 추가
 

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.simplexml.SimpleXmlConverterFactory;

public class NetworkProvider {

    private static final String API_BASE_URL = "http://apis.data.go.kr/";
    private final Retrofit retrofit;

    public NetworkProvider() {
        retrofit = new Retrofit.Builder()
                .baseUrl(API_BASE_URL)
                .client(new OkHttpClient())
                .addConverterFactory(SimpleXmlConverterFactory.create())
                .build();
    }

    public static NetworkProvider getInstance() {
        return Singleton.instance;
    }

    public EvChargerApi getEvChargerApi() {
        return retrofit.create(EvChargerApi.class);
    }

    private static class Singleton {
        private static final NetworkProvider instance = new NetworkProvider();
    }
}

 

Repository 클래스 추가
 

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class EvChargerRepository {

    interface Listener {
        void onSuccess(List<EvCharger> evChargers);

        void onFailure(ResponseError error);
    }

    private final EvChargerApi evChargerApi;

    public EvChargerRepository() {
        evChargerApi = NetworkProvider.getInstance().getEvChargerApi();
    }

    public void getEvChargeStations(Listener listener) {
        evChargerApi.getChargerInfo().enqueue(new Callback<ChargerInfoResponse>() {
            @Override
            public void onResponse(Call<ChargerInfoResponse> call, Response<ChargerInfoResponse> response) {
                if (!response.isSuccessful()) {
                    listener.onFailure(new ResponseError("999", "Error: " + response.code()));
                    return;
                }

                if (response.body().isSuccess()) {
                    listener.onSuccess(response.body().getStations());
                    return;
                }

                listener.onFailure(new ResponseError(response.body().getResultCode(), response.body().getResultMsg()));
            }

            @Override
            public void onFailure(Call<ChargerInfoResponse> call, Throwable t) {
                listener.onFailure(new ResponseError("99", t.getMessage()));
            }
        });
    }
}


Activity
 

    private EvChargerRepository evChargerRepository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stations);

        evChargerRepository = new EvChargerRepository();
        evChargerRepository.getEvChargeStations(new EvChargerRepository.Listener() {
            @Override
            public void onSuccess(List<EvCharger> evChargers) {
                for (EvCharger evCharger : evChargers) {
                    System.out.println("===== " + evCharger);
                }
            }

            @Override
            public void onFailure(ResponseError error) {
                System.out.println("===== onFailure:" + error);
            }
        });
    }
}

도움이 되시길.

spark (227,530 포인트) 님이 2022년 7월 7일 답변
Repository 의 Listener는 라이프사이클을 고려할거면 위처럼 메소드에 집어넣지 말고 EvChargerRepository의 클래스 레벨에 setListener 형태로 만들고, Activity의 onStart에서 setListener를, onStop에서 null을 설정해 줍니다.


public class EvChargerActivity extends AppCompatActivity implements EvChargerRepository.Listener {

    ...


    @Override
    public void onSuccess(List<EvCharger> evChargers) {
       ...
    }

    @Override
    public void onFailure(ResponseError error) {
        ...
    }

    @Override
    protected void onStart() {
        super.onStart();
        evChargerRepository.setListener(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        evChargerRepository.setListener(null);
    }

}

public class EvChargerRepository {

    ...

    private Listener listener;

    ...
    public void setListener(Listener listener) {
        this.listener = listener;
    }

    public void getEvChargeStations() {
        evChargerApi.getChargerInfo().enqueue(new Callback<ChargerInfoResponse>() {
            @Override
            public void onResponse(Call<ChargerInfoResponse> call, Response<ChargerInfoResponse> response) {
                if (!response.isSuccessful()) {
                    if (listener != null)
                        listener.onFailure(new ResponseError("999", "Error: " + response.code()));
                    return;
                }

                if (response.body().isSuccess()) {
                    if (listener != null)
                        listener.onSuccess(response.body().getStations());
                    return;
                }

                if (listener != null)
                    listener.onFailure(new ResponseError(response.body().getResultCode(), response.body().getResultMsg()));
            }

            @Override
            public void onFailure(Call<ChargerInfoResponse> call, Throwable t) {
                if (listener != null)
                    listener.onFailure(new ResponseError("99", t.getMessage()));
            }
        });
    }
}
...