안드로이드 스레드 XML 리스트 뷰 (1) : Android XML ListView Adapter Http AsyncTask
"안드로이드 XML 네트워크 리스트 뷰"
만년만에 포스팅... 잠시 개인 프로젝트 개발에 열중하다보니 시간이 없었습니다.
최근 다시 안드로이드를 개발하고 있으며, 개발 중 테스트용으로 개발된 정보를 정리하여 포스팅하였습니다.
이번 포스팅은 XML 을 HTTP 프로토콜로 호출하여 파싱 후 리스트뷰 출력하는 부분까지 작성되었습니다.
* 개발환경
- 안드로이드 버전
minSdkVersion : 16 (Android 4.1.2)
targetSdkVersion 18 (Android 4.3)
- 개발 플렛폼
Mac OS X 10.8.5
ADT (Android Developer Tools Build: v22.2.1-833290)
* 알아두기
- 안드로이드 네트워크를 사용하려면 인터넷 권한을 활성화해야 합니다.
오류 메세지 : java.lang.SecurityException: Permission denied (missing INTERNET permission?)
AndroidManifest.xml 에서 <uses-permission android:name="android.permission.INTERNET"/> 추가해야 합니다.
문제점 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202037499
퍼미션 설정 참고 : http://ljsk139.blog.me/30177948090
- 또한 네트워크를 이용할때는 꼭 스레드에서 작업을 해야합니다. 그렇지 않을 경우 오류가 발생합니다.
오류 메세지 : android.os.NetworkOnMainThreadException
참고 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202037396
- 안드로이드 에뮬레이터 AVDM 한글문제
4.2.x 와 4.3 에서 한글을 지원하지 않습니다. 언제 업그래이드 될지는 모르나... 현재는 그렇더군요.
그래서 최소 버전을 16 대상 버전을 18로 설정하고 개발하였습니다.
문제점 : http://tweakerz.blog.me/40198483134
한글폰트 추가하기 : http://jpub.tistory.com/336
- resource 는 첨부파일을 참조하고 자바 소스만 포스팅하겠습니다. (XmlBean 클래스도 생략합니다)
* 소스압축 파일
결과화면
HTTP 프로토콜 클래스
Apache HttpComponents 를 사용하였습니다.
특정 url 에 접속하여 결과를 InputStream 받아서 반환하는 작업을 담당합니다.
/* * HttpConnection.java * * Copyright (c) 2013, Seok Kyun. Choi. (최석균) * http://syaku.tistory.com * */ package com.syaku; import java.io.InputStream; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.util.Log; public class HttpConnection { String url = null; HttpResponse response = null; HttpEntity entity = null; InputStream input_stream = null; public HttpConnection(String url) { this.url = url; Log.i("HttpConnection RUN",url); try { HttpGet request = new HttpGet(url); HttpClient httpclient = new DefaultHttpClient(); this.response = httpclient.execute(request); this.entity = this.response.getEntity(); this.input_stream = this.entity.getContent(); } catch (Exception e) { Log.e("HttpConnection ERROR",e.toString()); } } void setEntity(HttpEntity entity) { this.entity = entity; } public HttpResponse getResponse() { return this.response; } public HttpEntity getEntity() { return this.entity; } public InputStream getInputStream() { return this.input_stream; } }
Xml 파서 클래스
안드로이드에서 제공하는 org.w3c.dom 을 사용하였습니다.
ArrayList 에 HashMap 을 사용하지 않고 SparseArray 사용하였습니다.
안드로이드에서는 SparseArray를 권장합니다.
참고 : http://www.mnworld.co.kr/1638
기본적인 xml 데이터 구조
<data>
<item>
<a> ... <- get(0)
<b> ... <- get(1)
... <- get(x)
</item>
<item>
<a> ... <- get(0)
<b> ... <- get(1)
... <- get(x)
</item>
</data>
InputStream 자료를 읽어 ArrayList 로 반환하는 작업을 담당합니다.
/* * Xml2Object.java * * Copyright (c) 2013, Seok Kyun. Choi. (최석균) * http://syaku.tistory.com * */ package com.syaku; import java.io.InputStream; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import android.util.Log; import android.util.SparseArray; public class Xml2Object { Document doc = null; public Xml2Object(InputStream input_stream) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); this.doc = builder.parse(input_stream); } catch (Exception e) { } } public ArrayList<SparseArray<String>> list(String name) { ArrayList<SparseArray<String>> list = new ArrayList<SparseArray<String>>(); try { Element root = doc.getDocumentElement(); NodeList node_list = root.getElementsByTagName(name); if (node_list == null) { return list; } int count = node_list.getLength(); Log.d("Node List Count",""+count); for (int i = 0; i < count; i++) { NodeList items = node_list.item(i).getChildNodes(); int item_count = items.getLength(); SparseArray<String> data = new SparseArray<String>(); int key = 0; for (int x = 0; x < item_count; x++) { Node node = items.item(x); if (node != null) { //String node_name = node.getNodeName(); String node_value = ""; Node node_text = node.getFirstChild(); if (node_text != null) { node_value = node_text.getNodeValue(); } data.append(key, node_value); //Log.d(key + ") Item Node name",node_name); //Log.d(key + ") Item Node value",node_value); key++; } } if (data != null) { list.add(data); } } Log.d("list data add",list.size() + "ea success"); } catch (Exception e) { Log.e("list Error",e.toString()); } return list; } public SparseArray<String> object(String name) { SparseArray<String> data = new SparseArray<String>(); NodeList node_list = doc.getElementsByTagName(name); if (node_list == null) { return data; } NodeList items = node_list.item(0).getChildNodes(); int count = items.getLength(); Log.d("Item Node Count",""+count); int key = 0; for (int i = 0; i < count; i++) { Node node = items.item(i); if (node != null) { //String node_name = node.getNodeName(); String node_value = ""; Node node_text = node.getFirstChild(); if (node_text != null) { node_value = node.getFirstChild().getNodeValue(); } //Log.d("Item Node name",node_name); //Log.d("Item Node value",node_value); data.append(key, node_value); key++; } } return data; } }
메인 액티비티 클래스
모든 작업을 호출하고 결과를 출력하는 작업을 담당합니다.
네트워크로 작업할때는 꼭 스레드를 사용해야 합니다.
멀티테스킹 AsyncTask 클래스를 사용했습니다.
참고 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202038678
XmlListAsyncTask 클래스는 스레드를 이용하여 Xml 데이터를 불러오는 작업을 담당합니다.
package com.syaku; import java.io.InputStream; import java.util.ArrayList; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.util.SparseArray; import android.widget.ListView; import android.widget.TextView; import android.app.Activity; import android.content.Context; public class SyakuMain extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.syaku_main); // XML 데이터 호출 XmlListAsyncTask xml = new XmlListAsyncTask(this); String url = "http://xxx.xml"; xml.execute(url); } // AsyncTask 스레 class XmlListAsyncTask extends AsyncTask< String, Void , ArrayList<SparseArray<String>> > { Context context; ArrayList<XmlBean> list_item; public XmlListAsyncTask(Context context) { this.context = context; } @Override protected ArrayList<SparseArray<String>> doInBackground(String... params) { ArrayList<SparseArray<String>> list = new ArrayList<SparseArray<String>>(); try { // xml 호 String url = params[0]; HttpConnection http = new HttpConnection(url); InputStream input_stream = http.getInputStream(); //xml 파 Xml2Object xml = new Xml2Object(input_stream); list = xml.list("item"); } catch (Exception e) { Log.e("GET ERROR",e.toString()); } return list; } @Override protected void onPostExecute(ArrayList<SparseArray<String>> result) { super.onPostExecute(result); int count = result.size(); Log.d("ArrayList count",""+count); TextView tv = (TextView) findViewById(R.id.list_count); tv.setText(""+count); list_item = new ArrayList<XmlBean>(); for (int i = 0; i < count; i++) { SparseArray<String> data = result.get(i); XmlBean bean = new XmlBean(); String title = data.get(bean.TITLE); String regdate = data.get(bean.REGDATE); bean.setTitle(title); bean.setRegdate(regdate); list_item.add(bean); } XmlListAdapter adapter = new XmlListAdapter(context, R.layout.list, list_item); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); } } }
XmlListAdapter 클래스
ArrayList 자료를 받아 뷰에 목록 형식으로 출력 작업을 담당합니다.
하나의 항목이 목록으로 출력되는 형식이 아닌 다양한 항목이 하나의 목록에 출력될 수 있게 작업된 구조입니다.
아래의 클래스는 제목과 날짜를 항목을 출력합니다.
package com.syaku; import java.util.ArrayList; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; public class XmlListAdapter extends BaseAdapter { Context context; LayoutInflater inflater; ArrayList<XmlBean> bean; int layout; public XmlListAdapter(Context context, int layout, ArrayList<XmlBean> bean) { this.context = context; this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.layout = layout; this.bean = bean; } @Override public int getCount() { return bean.size(); } @Override public String getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int postion, View view, ViewGroup view_group) { final int pos = postion; if (view == null) { view = inflater.inflate(layout,view_group,false); } TextView tv; tv = (TextView) view.findViewById(R.id.title); tv.setText(bean.get(pos).title); tv = (TextView) view.findViewById(R.id.regdate); tv.setText(bean.get(pos).regdate); return view; } }
다음 포스팅은 목록을 클릭하여 해당 자료 상세보기 액티비티로 전환되는 과정을 설명하겠습니다.