오늘은 Android Tablet Developer's Cook Book에 나오는 JSON 관련 예제를 공부할 겁니다.
Chapter 15에 나오는 예제인데요. ConsumeJSONWebserviceApp 이라는 프로젝트 입니다.
일단 SampleJSON.php 라는 파일이 아래 경로에 있습니다.
http://www.gosolkit.com/SampleJSON.php
이 php가 만들어낸 json 데이터는 아래와 같습니다.
[{"state":"Alabama","capital":"Montgomery","latitude":"32.361538","longitude":"-86.279118"},{"state":"Alaska","capital":"Juneau","latitude":"58.301935","longitude":"-134.419740"},{"state":"Arizona","capital":"Phoenix","latitude":"33.448457","longitude":"112.073844"},{"state":"California","capital":"Secramento","latitude":"36.448457","longitude":"112.073844"},{"state":"Gangwon","capital":"Chunchon","latitude":"33.448457","longitude":"112.073844"},{"state":"Rhode Island","capital":"Providence","latitude":"33.448457","longitude":"112.073844"},{"state":"New Jersey","capital":"Trenton","latitude":"33.448457","longitude":"112.073844"}]
위 URL은 책에 있는 URL이랑은 다릅니다.
제 개인 URL 을 사용했구요. json 데이터도 안에 그 내용을 조금 더 추가했습니다.
원격에 있는 php로 생성한 JSON 파일을 받아와서 안드로이드 앱에서 사용하는 예제 입니다.
우선 첫번째로 layout xml 파일을 보죠.
activity_consume_jsonwebservice_app.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/state_name"
android:hint="Enter State Name"
android:textSize="@dimen/text_size" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/submit_btn"
android:text="Submit"
android:textSize="@dimen/text_size" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/response"
android:textSize="@dimen/text_size" />
</LinearLayout>
LinearLayout으로 감쌌고 orientation은 vertical 입니다.
그 안에 EditText와 Button 그리고 TextView 이렇게 세개의 view 가 있습니다.
EditText에 주 이름을 넣고 Submit 버튼을 누르면 그 아래 TextView에 원격에서 받은 JSON 데이터를 파싱해서 뿌려 줄 건가 봅니다.
그러면 이제 자바 파일을 보죠.
ConsumeJSONWebserviceAppActivity.java
public class ConsumeJSONWebserviceAppActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_consume_jsonwebservice_app);
Button submitButton = (Button)this.findViewById(R.id.submit_btn);
submitButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v) {
new ReadJSONFeed().execute("http://www.gosolkit.com/SampleJSON.php");
}
});
}
private class ReadJSONFeed extends AsyncTask<String, String, String> {
protected void onPreExecute() {}
@Override
protected String doInBackground(String... urls) {
HttpClient httpclient = new DefaultHttpClient();
StringBuilder builder = new StringBuilder();
HttpPost httppost = new HttpPost(urls[0]);
try {
HttpResponse response = httpclient.execute(httppost);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return builder.toString();
}
protected void onPostExecute(String result) {
String state="";
String stateInfo="";
EditText stateName = (EditText) findViewById(R.id.state_name);
String searchState=stateName.getText().toString();
try{
JSONArray countriesArray = new JSONArray(result);
for (int i =0 ; i<countriesArray.length();i++) {
JSONObject jObject = countriesArray.getJSONObject(i);
state = jObject.getString("state");
if(searchState.equalsIgnoreCase(state))
{
stateInfo+="Capital: "+jObject.getString("capital")+"\n";
stateInfo+="Latitude: "+jObject.getString("latitude")+"\n";
stateInfo+="Longitude: "+jObject.getString("longitude")+"\n";
}
}
}
catch (JSONException e) {
e.printStackTrace();
}
TextView resp = (TextView) findViewById(R.id.response);
if(stateInfo.trim().length() >0 )
resp.setText(stateInfo);
else
resp.setText("Sorry no match found");
}
}
}
그렇게 복잡한거 같지는 않죠?
먼저 onCreate()를 보죠.
activity_consume_jsonwebservice_app.xml 을 Layout 으로 사용한다고 세팅했습니다.
바로 위에서 본 xml 입니다.
그리고 버튼에 대한 객체를 만들고 이 버튼에 onClickListener를 달았습니다.
이 버튼을 클릭하면 ReadJSONFeed() 라는 클래스를 호출하네요. 그리고 excute() 라는 메소드에 아까 소개했던 원격 URL 을 pass 합니다.
그러면 ReadJSONFeed() 클래스를 보겠습니다.
이 클래스는 AsyncTask 를 상속합니다.
여기를 보시면 해당 API를 보실 수 있습니다.
UI thread를 쉽게 사용할 수 있게 해 준다고 하네요. background 로 실행되게 해서 그 결과를 UI thread에 publish 하는 군요. 따로 thread나 handler들을 작성하지 않아도 된다고 하네요.
이 AsyncTask는 짧은 기간 동안 수행할 작업에 사용됩니다. 만약 긴 기간동안 수행할 작업을 쓰레드로 사용하시려면 Executor
,
ThreadPoolExecutor
and FutureTask
같은 java.util.concurrent
pacakge 를 사용 하셔야 합니다.
이 asynchronous task(비동기 작업)은 Params
, Progress
and Result
이렇게 3가지가 있고 4개의 step들이 있습니다.
onPreExecute
, doInBackground
,
onProgressUpdate
and onPostExecute
그러면 계속 ReadJSONFeed 클래스를 보겠습니다.
첫번째 줄에는 위에 봤든 onPreExecute() 메소드가 있네요. 그 안에는 아무것도 없구요.
그냥 넘어가도 되겠습니다.
그 다음에는 또 위에서 봤던 doInBackground() 메소드가 나옵니다.
이 안에 어떤 로직을 넣으면 Background 로 돌아가게 됩니다.
우선 HTTPClient와 HttpPost, StringBuilder 를 초기화 했습니다.
HttpResponse를 초기화 하면서 httpclient의 execute메소드를 통해 httppost를 pass 해 줍니다.
여기 httppost에는 아까 버튼 눌렀을 때 pass 했던 url이 들어 있습니다.
그러면 response 안에 그 결과값 즉 json 데이터들이 담기겠죠.
그 다음에는 StatusLine을 초기화 하면서 이 response의 line을 get 합니다.
즉 json 내용을 get 하는거죠.
그리고 이 statusLine 의 statusCode를 statusCode라는 변수에 담습니다.
이 statusCode에는 성공했는지 실패 했는지에 대한 정보가 담기겠죠.
다음 if 문에서는 만약에 이 작업이 성공했으면 HttpEntity 를 생성해서 여기에 response의 Entity를 가져 옵니다. 그리고 그 내용을 InputStrean에 담고요.
다음에 BufferedReader를 이용해서 그 내용을 읽습니다.
Java에서 파일 내용을 읽어 들일 때 사용하는 InputStream과 BufferedReader가 여기서도 사용되네요.
그 다음에 line이라는 String을 만들어서 모든 줄을 이 line에 넣습니다.
여기까지가 doInBackground() 메소드가 하는 일입니다.
간단하네요. 원격에 있는 정보를 가져와서 이것을 사람이 읽을 수 있는 정보로 line이라는 String에 담은 겁니다.
그 다음에 실행되는 메소드는 onPostExecute() 입니다.
한번 볼까요?
state, stateInfo 라는 String을 만들었습니다. 주 이름과 그 주에 대한 정보를 담을 변수들 인 것 같습니다.
그리고 layout xml에서 만든 EditText에 대한 변수를 만들었습니다.
그리고 searchState라는 String에 이 stateName의 내용을 담을 겁니다.
다음에 나오는 내용들은 JSON을 파싱하는 로직 입니다.
아까 doInBackGround에서는 line이라는 String에 이 JSON 데이터를 그냥 담았죠.
이것을 파싱해서 사람이 알아볼 수 있도록 화면에 뿌려줄 겁니다.
onPostExecute(String result) 라고 위에 돼 있죠. 여기서는 pass 받은 string을 result 라는 변수이름으로 사용할 겁니다.
보시면 이 result 를 countriesArray라는 JSONArray 에 담습니다.
그 다음 for 문을 돌려서 아까 만들었던 변수들에 해당 내용들을 담습니다.
state에는 주 이름이 그리고 searchState에 있는 내용들은 Capital, Latitude, Longitute 별로 따로 사람이 읽기 좋게 편집해서 stateInfo에 담습니다.
그 다음에는 try catch 문 밖을 보시면 되는데요.
TextView 객체를 만들었죠.
이 text 뷰에 위에 만들었던 stateInfo를 setText() 해서 화면에 표시합니다.
그러면 아래와 같은 화면을 보실 수 있습니다.
여기서의 주요 개념은 AsyncTask 를 이용해서 쉽게 Background 작업을 하도록 한다는 것과
원격의 데이터를 받아 오는 방법 여기서는 HttpClient, StringBuilder, HttpPost, HttpResponse 등이 사용됐습니다. 그리고 이 데이터를 바이너리 형태로 돼 있을 텐데 이것을 InputStream과 BufferedReader를 이용해서 사람이 읽을 수 있는 문자로 파싱하는 방법을 배웠습니다.
그리고 그 파싱된 JSON 타입을 다시 key 값을 이용해서 value를 가져와서 필요에 따라 편집하는 방법도 배웠습니다.
아주 중요한 개념들이 많네요.
안드로이드 앱과 웹 서비스와의 네트워킹도 아주 쉽게 사용할 수 있도록 만들었군요.
'WEB_APP > Android' 카테고리의 다른 글
[Cookbook] 안드로이드 Cores 와 Threads 이해하기 -5- (0) | 2013.09.25 |
---|---|
[Cookbook] 안드로이드 Cores 와 Threads 이해하기 -4- (0) | 2013.09.24 |
[Cookbook] 안드로이드 Cores 와 Threads 이해하기 -3- (0) | 2013.09.24 |
[Cookbook] 안드로이드 Cores 와 Threads 이해하기 -2- (0) | 2013.09.23 |
[Cookbook] 안드로이드 Cores 와 Threads 이해하기 -1- (0) | 2013.09.23 |
[CookBook] Tweening Animation 소스 분석하기 -2- (0) | 2013.09.19 |
[CookBook] Tweening Animation 소스 분석하기 -1- (0) | 2013.09.18 |
[PageSliding] SamplePageSliding 소스 파일 분석하기 (0) | 2013.09.17 |
[ViewPager] SampleVIewPager 소스 파일 분석하기 -2- (0) | 2013.09.16 |
[ViewPager] SampleVIewPager 소스 파일 분석하기 -1- (0) | 2013.09.15 |