원본출처: 티스토리 바로가기
앱에서 지원하고 싶은 것중 우선 나의 주변에 버스 정류소를 찾는다. 어떻게 찾을까 ? 모든 데이터는 data.go.kr 공공데이터 포털을 중심으로 ... 찾아보니 제주도에서 제공하는 버스 정보가 있다.
http://www.jeju.go.kr/help/open.htm?page=3&act=view&seq=967654
다만, 가이드의 정보를 기준으로 보면 http:// 으로 시작하는 기본url 과 xml 형식으로 자료를 전송한다는 것이 조금 예전 방식인 것 같은 생각이 들었다.
이제 앱에 retrofit 통신을 하기 위한 준비를 해 보자. gradle 파일에 수정을
// 데이터 주고 받기 implementation 'com.squareup.retrofit2:retrofit:2.7.2' implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'
retrofit 과 xml 파싱을 위해서 추가 했다.
다음은 manifest 에 internet 접속을 위한 권한 등록
<uses-permission android:name="android.permission.INTERNET" />
이제 준비는 되었고. 그럼 데이터를 가져오기 위해서 일단 open api 가 제공하는 데이터 구조를 확인해 보아야겠다.
데이터는 이렇게 조회가 되고 있고, open api 가이드의 내용도 이와 같다. 그래서 일단은 데이터를 받아올 class 을 만들어 보았다.
맨 먼저 제일 바깥쪽에 구성되는 class 부터
import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; @Root(name="response", strict = false) public class StationBean { @Element(name="header") HeaderClass headerClass; @Element(name="body") BodyClass bodyClass ; public HeaderClass getHeaderClass() { return headerClass; } public BodyClass getBodyClass() { return bodyClass; } public void setHeaderClass(HeaderClass headerClass) { this.headerClass = headerClass; } public void setBodyClass(BodyClass bodyClass) { this.bodyClass = bodyClass; } }
Root 는 response 로 구성 되며, 그 안에는 header 와 body 가 들어간다.
다음은 header class
import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; @Root(name="header", strict = false) public class HeaderClass { @Element(name="resultCode") String resultCode; @Element(name="resultMsg") String resultMsg ; public String getResultCode() { return resultCode; } public String getResultMsg() { return resultMsg; } public void setResultCode(String resultCode) { this.resultCode = resultCode; } public void setResultMsg(String resultMsg) { this.resultMsg = resultMsg; } }
header 에는 resultCode, 와 resultMsg 만 들어 있고, 실제 데이터는 body 에 들어간다. 다음은 body class
import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Root; import java.util.ArrayList; @Root(name="body", strict = false) public class BodyClass { @ElementList(entry = "items") ArrayList<ItemClass> items ; @Element(name="numOfRows") String numOfRows ; @Element(name="pageNo") String pageNo ; @Element(name="totalCount") String totalCount ; public ArrayList<ItemClass> getItems() { return items; } public String getNumOfRows() { return numOfRows; } public String getPageNo() { return pageNo; } public String getTotalCount() { return totalCount; } public void setItems(ArrayList<ItemClass> items) { this.items = items; } public void setNumOfRows(String numOfRows) { this.numOfRows = numOfRows; } public void setPageNo(String pageNo) { this.pageNo = pageNo; } public void setTotalCount(String totalCount) { this.totalCount = totalCount; } }
body 에는 데이터가 들어가는 items 와 건수등을 표시하는 데이터 들이 들어 있는데, 구현하면서 조금 방황(?)한 부분은 ArrayList 로 표시하는 items 에 대한 구현을 어떻게 해야 하는 가에 대한 부분 이었다. gson 으로 파싱을 할 때는 그런거 없이 수월했는데, xml 형식에서는 EntryList 가 표현이 되도록 구현을 해야 되는 것이었다.
다음은 item이 들어가는 item class
import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; @Root(name="item", strict = false) public class ItemClass { @Element(name="dirTp") String dirTp ; @Element(name="govNm") String govNm; @Element(name="localX") double localX ; @Element(name="localY") double localY ; @Element(name="mobiNum") String mobiNum ; @Element(name="stationId") String stationId ; @Element(name="stationNm") String stationNm ; @Element(name="upd") String upd ; @Element(name="useYn") String useYn ; public double getLocalX() { return localX; } public double getLocalY() { return localY; } public String getMobiNum() { return mobiNum; } public String getDirTp() { return dirTp; } public String getGovNm() { return govNm; } public String getStationId() { return stationId; } public String getStationNm() { return stationNm; } public String getUpd() { return upd; } public String getUseYn() { return useYn; } public void setDirTp(String dirTp) { this.dirTp = dirTp; } public void setGovNm(String govNm) { this.govNm = govNm; } public void setLocalX(double localX) { this.localX = localX; } public void setLocalY(double localY) { this.localY = localY; } public void setMobiNum(String mobiNum) { this.mobiNum = mobiNum; } public void setStationId(String stationId) { this.stationId = stationId; } public void setStationNm(String stationNm) { this.stationNm = stationNm; } public void setUpd(String upd) { this.upd = upd; } public void setUseYn(String useYn) { this.useYn = useYn; } }
이렇게 구현 하면 데이터를 받아올 준비는 되었다. 이제 interface 을 구현해 볼 차례 인데, openapi 에는 호출시 파라미터를 last 와 serviceKey 를 받는다고 되어 있지만, 현재( 2021.12.19 )에는 아무것도 전달하지 않아도 되고, last 뒤에 날자만 전달해도 된다. last 을 넣어보내면 최근 업데이트 된 정보만 받아오는 것으로 확인이 되었다. 다만, 나의 앱에서는 정보가 처음이라, 전체 데이터를 받기 위해서 아무것도 파라미터로 사용하지 않다.
import retrofit2.Call; import retrofit2.http.GET; public interface RetrofitApi { String baseURL = "http://busopen.jeju.go.kr"; String getStation = "/OpenAPI/service/bis/Station"; @GET(getStation) Call<StationBean> getStationInfo(); }
interface class 는 이렇게 구성을 했다. query 가 없어서 아무것도 전달하지는 않았다.
이번에는 activity 에서 호출을 해 보도록 하겠다.
public class MainActivity extends AppCompatActivity { ... Retrofit retrofit ; RetrofitApi service ; ... private void doGetBusStation() { retrofit = new Retrofit.Builder() .baseUrl(RetrofitApi.baseURL) .client(new OkHttpClient()) .addConverterFactory(SimpleXmlConverterFactory.create()) .build(); service = retrofit.create(RetrofitApi.class); service.getStationInfo().enqueue(new Callback<StationBean>() { @Override public void onResponse(Call<StationBean> call, Response<StationBean> response) { Log.e(TAG, "response=" + response.code() + " Total=" + response.body().getBodyClass().getTotalCount()); for(ItemClass item : response.body().getBodyClass().getItems()) { Log.e(TAG, item.getStationNm()); } } @Override public void onFailure(Call<StationBean> call, Throwable t) { Log.e(TAG, "ERROR=" + t.toString()) ; } }); } ... }
아직 데이터를 받아와서 어떻게 하고자 하는 지 결정을 하지 못했다. 일단, 데이터가 들어 오는 것은 확인을 하였다. 이 구현을 하는데, CLEARTEXT communication to XXXX not permitted by network security policy 을 만나게 되면, 어떻게 할 것인가를 고민하지 말고 다음을 따라해 보면 된다. 이유는 http:// 으로 된 주소에 통신을 시도하기 때문에 발생하는 보안 문제인데, 나 앱이 모든 호출에서 http:// 로 할 것이 아니기 때문에 특정 url 만 http 로 호출할 것이라고 등록을 해 주면 된다.
먼저 xml 파일을 하나 만든다. res/xml/network_config.xml 이라고 .. 그안에는 다음과 같이 넣어 준다.
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">busopen.jeju.go.kr</domain> </domain-config> </network-security-config>
특정 주소는 http 로 통신을 하겠다는 것이다. 보안정책 때문에 누군가 싫어할지 모르지만.
그리고 다음은 manifest 에 다음을 추가해 준다.
<application android:allowBackup="true" android:icon="@mipmap/ic_info_logo" android:label="@string/app_name" android:roundIcon="@mipmap/ic_info_logo_round" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_config"
networkSecurityConfig 을 추가해 주면 끝.
제주도에는 4358개이 정류소 정보가 있나 보다. 다음엔 이것을 저장해서 데이터로 활용해 보도록 하겠다.
댓글
댓글 쓰기