API는 대량의 문자열 정보를 가지고 있다
이 대량의 대이터를 구분하기 위한 방법이 필요함
Open API 제공 기관에서 데이터를 줄때 각 값들을 구분하기 위해 3가지 데이터 표기형식(Representation of Resource)을 사용하였음
sam,robin,hong...
,를 하용해 데이터를 구분함
자바에서 split()을 사용해 손쉽게 배열객체로 만듦
💡 [단점]
데이터 자체에 무엇을 나태내는지 표기가 없어서 헷갈림 그래서 나온 것이 xml 방식
마크업을 통해 글씨를 씀
태그문<></>을 이용해서 정보를 구분해 담음
리사이클뷰에 보여주면됨
<item>
<title>왕십리약국</title>
<addr>서울시 도선동</addr>
<tel>02-214-2356</tel>
</item>
<item>
<title>이수약국</title>
<addr>서울시 사당동</addr>
<tel>02-456-7892</tel>
</item>
xml 분석하는 class 존재 (Xml~Parser)
① res 폴더에서 가져온다 => XmlResourceParser
② 서베용 분석가 => XmlPullParser
💡 xml가 읽는 방법
특정 단위별로 읽음, next할때마다 읽는 걸 이벤트라고 부름
단위 5가지(이벤트 타입)
1. 스타트 도큐먼트
2. 스타크 태그
3. TEXT
4. 엔드 태그
5. 엔트 도큐먼트
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="parse xml from resource"/>
<TextView
android:id="@+id/tv"
android:textColor="@color/black"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<movies>
<item>
<no>1</no>
<title>Alien</title>
<ganre>SF, ACTION, ADVENTURE</ganre>
</item>
<item>
<no>2</no>
<title>AVATA</title>
<ganre>SF, ACTION, ADVENTURE</ganre>
</item>
<item>
<no>3</no>
<title>Notting Hill</title>
<ganre>ROMENCE, MELO</ganre>
</item>
<item>
<no>4</no>
<title>Nightmare</title>
<ganre>HORROR, THRILLER</ganre>
</item>
</movies>
① res폴더 창고관리자 소환
Resources res = getResources();
② 창고 관리자로부터 파서 객체 얻어오기
XmlResourceParser xrp = res.getXml(R.xml.movies);
③ xml 파서에게 분석 작업 요청
package com.bsj0420.ex52xmlresouceparsing;
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.widget.TextView;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
findViewById(R.id.btn).setOnClickListener(view -> clickBtn());
}
void clickBtn(){
//Res 폴더에 있는 xml 문서를 읽어와서 분석(parse)하는 작업 수행
//1. res폴더 창고관리자 소환
Resources res = getResources();
//2. 창고 관리자로부터 파서 객체 얻어오기
XmlResourceParser xrp = res.getXml(R.xml.movies);
//3. xml 파서에게 분석 작업 요청
try {
xrp.next(); //스트림 작업은 위험 - 예외처리 해야됨
int eventType = xrp.getEventType(); //리턴값 int 단위 5가지를 숫자로 리턴받음
//스트링을 가지고 있는 작은 저장 공간
StringBuffer buffer = new StringBuffer();
// buffer.append("aa"); //append()로 쌓아둠
//
// String s = buffer.toString(); //이때 메모리에 객체 만듦!!!
// while (true) {
//
// xrp.next();
// eventType = xrp.getEventType(); //무한이 돌면서 eventType 받아와라
//
// if(eventType == XmlResourceParser.END_DOCUMENT) break;
// }
// 축약 1 : next()하면 그 순간 이벤트 값이 오기때문에 getEventType() 따로 쓸 필요없음
// while (true) {
// eventType = xrp.next();
// if(eventType == XmlResourceParser.END_DOCUMENT) break;
// }
//축약 2 : if 조건을 () 안으로
while (eventType != XmlResourceParser.END_DOCUMENT) {
switch (eventType) {
case XmlResourceParser.START_DOCUMENT:
buffer.append("----파싱 시작---- \n\n");
break;
case XmlResourceParser.END_DOCUMENT:
break;
case XmlResourceParser.START_TAG:
String tagName = xrp.getName(); //태그 이름이 많으니까 구분하기 위해
if(tagName.equals("item")) {
//아무것도 안썼으니 그냥 넘어감
} else if (tagName.equals("no")) {
buffer.append("순위 : ");
} else if (tagName.equals("title")) {
buffer.append("제목 : ");
} else if (tagName.equals("genre")) {
buffer.append("장르 : ");
}
break;
case XmlResourceParser.TEXT:
String text = xrp.getText(); //사이 텍스트값
buffer.append(text);
break;
case XmlResourceParser.END_TAG:
String tagName2 = xrp.getName();
if(tagName2.equals("item")){ //끝나는 태그랑 만났니?
buffer.append("\n---------------------\n");
} else if (tagName2.equals("no")) {
buffer.append("\n");
} else if (tagName2.equals("title")) {
buffer.append("\n");
} else if (tagName2.equals("genre")) {
buffer.append("\n");
}
break;
}
eventType = xrp.next();
}//
// 파싱 끝
buffer.append("\n\n -- 파싱을 완료했습니다-- \n\n");
tv.setText(buffer.toString()); //이때 메모리에 만듦
} catch (IOException e) {
throw new RuntimeException(e);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
}
//문자열을 계속 결합해야할땐
}
}
StringBuffer
StringBuffer buffer = new StringBuffer();
buffer.append("aa"); //append()로 쌓아둠
String s = buffer.toString(); //이때 메모리에 객체 만듦!!!
String s = "";
s += "aa";
s += "bb";
이렇게 String을 더하면 매번 메모리에 새로운 객체가 생성돼서 메모리를 쓸데없이 많이 쓰게됨 때문에 문자를 쌓아두다가 한번에 객체를 만드는 StringBuffer 를 사용한다
1.recycle가 잘 동작하는지 부터 확인 (recycle부터 만들기)
① main.xml 에 recycleView 만들기
② item class 만들기
package com.bsj0420.ex53xmlpullparsermovie;
public class MovieItem {
String rank;
String movieNm;
String openDt;
String audiAcc;
public MovieItem(){
}
public MovieItem(String rank, String movieNm, String openDt, String audiAcc) {
this.rank = rank;
this.movieNm = movieNm;
this.openDt = openDt;
this.audiAcc = audiAcc;
}
}
③ mian.java에서 가상의 데이터 넣고 recycleView 확인
package com.bsj0420.ex53xmlpullparsermovie;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
//영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
ArrayList<MovieItem> movieItems = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//원래는 서버의 xml을 읽어와야하지만
// recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));
}
}
④ adapter 만들기
package com.bsj0420.ex53xmlpullparsermovie;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.VH> {
Context context;
ArrayList<MovieItem> items;
public MovieAdapter(Context context, ArrayList<MovieItem> items) {
this.context = context;
this.items = items;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false);
return new VH(view);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
//현재 번째 데이터면 MovieItem얻어오기
MovieItem item = items.get(position);
holder.tvRank.setText(item.rank);
holder.tvTitle.setText(item.movieNm);
holder.tvOpenDt.setText(item.openDt);
holder.tvAudiAcc.setText(item.audiAcc);
}
@Override
public int getItemCount() {
return items.size();
}
class VH extends RecyclerView.ViewHolder {
TextView tvRank, tvTitle, tvOpenDt, tvAudiAcc;
public VH(@NonNull View itemView) {
super(itemView);
tvRank = itemView.findViewById(R.id.tv_rank);
tvTitle = itemView.findViewById(R.id.tv_title);
tvOpenDt = itemView.findViewById(R.id.tv_open_date);
tvAudiAcc = itemView.findViewById(R.id.tv_audiAcc);
}
}
}
⑤ 연결 확인
package com.bsj0420.ex53xmlpullparsermovie;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
//영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
ArrayList<MovieItem> movieItems = new ArrayList<>();
RecyclerView recyclerView;
MovieAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//원래는 서버의 xml을 읽어와야하지만
// recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));
//아답터 연결
recyclerView = findViewById(R.id.recycler);
adapter = new MovieAdapter(this,movieItems);
recyclerView.setAdapter(adapter);
}
}
② 네트워크 작업은 오래 걸리는 작업으로 인식됨 -> 반드시 별도의 스레드가 작업
new Thread().start();
//=> start() 끝나기 전에 {} 중괄호 열어서 해야할 일 쓴다
new Thread(){
@Override
public void run() {
//네트워크 작업 비동기로 시작.
}
}.start();
③ 위 서버 url 위치까지 무지개로드(stream)를 열어주는 해임달 객체() 생성
URL url = new URL(address); //스트림은 예외처리 필수
④ url 통해 무지개 로드(스트림) 열기
InputStream is =url.openStream(); //문자를 바이트 단위로 읽어옴
InputStreamReader isr = new InputStreamReader(is); //바이트 => 문자로 변환
⑤ xml 문서를 조금 더 쉽게 분석(parse) 해주는 객체 생성
=> XmlPullParser 만들기 위한 공장 필요 (factory 빌드패턴)
XmlPullParser는 공장을 통해서 만들어야함
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xpp = factory.newPullParser();
⑥ XmlPullParser에게 스트림 주기
xpp.setInput(isr);
⑦ xml Parser에게 분석 작업
click() 메소드 안 잘 보기~!
package com.bsj0420.ex53xmlpullparsermovie;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
//영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
ArrayList<MovieItem> movieItems = new ArrayList<>();
RecyclerView recyclerView;
MovieAdapter adapter;
String apiKey = "f5eef3421c602c6cb7ea224104795888";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//원래는 서버의 xml을 읽어와야하지만
// recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
//movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));
//아답터 연결
recyclerView = findViewById(R.id.recycler);
adapter = new MovieAdapter(this,movieItems);
recyclerView.setAdapter(adapter);
findViewById(R.id.btn).setOnClickListener(view -> clickBtn());
}
void clickBtn() {
//영화진흥원의 open API 정보(일일 박스오피스)를 가져와서 리사이클러뷰에 보여주기
//xml파일 포멧으로 되어있음
//1. 네트워크 작업은 권한이 반드시 요구됨.[ AndroidManifest.xml 에서 권한부여 ]
//2. 네트워크 작업은 오래 걸리는 작업으로 인식됨
//반드시 별도의 스레드가 작업해야한다
new Thread(){
@Override
public void run() {
//네트워크 작업 비동기로 시작.
//xml문서의 REST url주소
//검색 날짜 [오늘 날짜의 전날]
Date date = new Date(); //이 객체가 생성되는 순간의 날짜와 시간을 가짐
//date.setTime(); // 19700101 0시 0분 0초가 기준
date.setTime(date.getTime() - (1000*60*60*24)); //하루전 시간 뺀 것
//특정 포멧으로 날짜를 문자열로 만들어주는 객체
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); //mm 쓰면 분 나옴
String yesterday = sdf.format(date);
// 샘플 주소
String address = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.xml"
+"?key="+apiKey
+"&targetDt="+yesterday
+"&itemPerPage=5";
// 위 서버 url 위치까지 무지개로드를 열어주는 해임달 객체 생성
try {
URL url = new URL(address); //스트림은 예외처리 필수
//무지개 로드(스트림) 열기
InputStream is =url.openStream(); //문자를 바이트 단위로 읽어옴
InputStreamReader isr = new InputStreamReader(is); //바이트 => 문자로 변환
//xml 문서를 조금 더 쉽게 분석(parse) 해주는 객체 생성
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
// => XmlPullParser 만들기 위한 공장 필요 factory 빌드패턴
XmlPullParser xpp = factory.newPullParser(); //공장을 통해서 만들어야함
// XmlPullParser에게 스트림 주기
xpp.setInput(isr);
//xml Parser에게 분석 작업
//XmlPullParser는 시작부터 스타트도큐먼트임
//따라서 .next(); 먼저 할 필요없다
int eventType = xpp.getEventType();
//영화 한개 정보 참조변수
MovieItem movieItem = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
//별도의 스레드는 ui작업 불가능 그래서 ui스레드에서 작업하도록 요청
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "파싱 시작", Toast.LENGTH_SHORT).show();
}
});
break;
case XmlPullParser.START_TAG:
String tagName = xpp.getName(); //태그이름 얻어오기
if(tagName.equals("dailyBoxOffice")){
movieItem = new MovieItem(); //생성자가 빈걸로 객체 생성 (빈통 만들어 놓기)
} else if (tagName.equals("rank")) {
xpp.next(); //rank 스타트 태크 만나면 바로 text 로 가라
movieItem.rank = xpp.getText(); //거기 써있는 글씨 가져와서 movieItem에 넣어
} else if (tagName.equals("movieNm")) {
xpp.next();
movieItem.movieNm = xpp.getText(); //XmlPullParser.TEXT 안해도 됨
} else if (tagName.equals("openDt")) {
xpp.next();
movieItem.openDt = xpp.getText();
} else if (tagName.equals("audiAcc")) {
xpp.next();
movieItem.audiAcc = xpp.getText();
}
break;
case XmlPullParser.END_TAG:
String tagName2 = xpp.getName();
if(tagName2.equals("dailyBoxOffice")){
//영화 하나 끝났으면
//리사이클러의 어레이리스트에 add
movieItems.add(movieItem);
}
break;
case XmlPullParser.TEXT:
break;
}
eventType = xpp.next();
}//while....
//UI작업은 ui thread 위에서 작업
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "파싱 완료", Toast.LENGTH_SHORT).show();
//대량의 데이터가 새로이 추가 됐다고 아답터한테 공지해야만
//리사이클러뷰가 화면을 개신한다
adapter.notifyDataSetChanged();
}
});
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
}
}
}.start();
}
}
Data 객체가 new 키워드로 생성되는 순간의 날짜와 시간을 가짐
Date date = new Date(); => 현재 시간을 가진 date
date.setTime(); // 19700101 0시 0분 0초가 기준
date.setTime(date.getTime() - (10006060*24)); //하루전 시간 뺀 것
특정 포멧으로 날짜를 문자열로 만들어주는 객체
1.포멧을 정한 객체 생성
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); //mm 쓰면 분 나옴
2.포멧 대입
String yesterday = sdf.format(date);
💡 [단점]
지금은 xml이 태그문때문에 너무 문자양이 길어져서 지금은 바꾸는 중.[개발자의 코드도 좀 지저분함]
지금은 json 이라는 표기문법을 사용함. - 이게 가장 많이 사용됨
파일 포멧 형식
{"name:sam","age:20"...} key-value 쌍으로 저장
[ {"title":"왕십리약국","addr":"서울","tel":1234} , {"title":"이수약국","addr":"인천","tel":8887} ]
그래서 기관마다 xml 또는 json 으로 정보를 제공함.
각각의 데이터만 주는 경우도 있고
둘 모두를 제공해 주는 경우도 있음.
① 특정한 컴퓨터의 정보를 가져오는 약속 (GET/POST 방식)
② 현재 이 데이터가 저장되어 있는 방식
③ URL + path 데이터가 저장되어 있는 위치
④ http://0/0/0?dataTitle=조광&pageSize=0 3번 뒤에 붙는 데이터 정보
⑤ 태그문으로 구분되어 있는 데이터들