앱에서 지원하고 싶은 것중 우선 나의 주변에 버스 정류소를 찾는다. 어떻게 찾을까 ? 모든 데이터는 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개이 정류소 정보가 있나 보다. 다음엔 이것을 저장해서 데이터로 활용해 보도록 하겠다.
'모바일 앱(안드로이드)' 카테고리의 다른 글
안드로이드 앱 만들기 : 음력을 양력으로 변환 하기 (3) | 2021.12.27 |
---|---|
안드로이드 앱 만들기 : 옵디강 (제주맛집) 기능 추가 버스 정류소 위치 (3) | 2021.12.26 |
안드로이드 앱 만들기 : 별점 주기 (0) | 2021.12.17 |
안드로이드 앱 만들기 : 알림 구현 방법 Snackbar 와 Toast 차이 (3) | 2021.12.10 |
안드로이드 앱 만들기 : 리사이클뷰(RecycleView) 에 광고 추가 (0) | 2021.12.08 |