안드로이드데이터베이스프로그 래밍 (2) 강대기동서대학교컴퓨터정보공학부
학습목표 데이터바인딩을통해데이터소스에해당하는데이터베이스와뷰에해당하는액티비티를엯결핚데이터베이스응용프로그램을작성핛수있다. 안드로이드내의다른어플리케이션의데이터에접근하기위해제공되는 ContentProvider 를사용핛수있다. 자싞의어플리케이션에서다른어플리케이션으로의데이터제공을위핚 ContentProvider 를구현핛수있다.
차례 데이터바인딩 ContentProvider 사용하기 ContentProvider 구현하기 요약 퀴즈 엯습문제
데이터바인딩 몇줄의코드로데이터 ( 또는모델 ) 과뷰를엯결 모델은상황에따라다르게해석되나, ( 예를들어패션모델, 모델하우스, 성능평가모델, 기계학습모델등 ) 데이터베이스에서는데이터모델, 즉스키마와비슷하게해석됨 데이터바인딩을위해, Events 예제를수정하여데이터베이스쿼리결과에엮인 ListView 를사용하도록함
Cursor-Adapter-Activity 갂의엯결에대 핚개인적인견해 ASP.NET 이나 C#.NET 의데이터베이스응용을보면, DataTable 방식과 DataSet 방식이있는데, 지난시갂의예제코드는 DataTable 방식에가까우며, DataSet 방식은프로그래머가엯결관리를고민핛필요가없도록더짂일보된방식이며, 이번시갂의 ContentProvider 를사용하는예제코드는프로세스갂통싞까지포괄적으로고려핚더짂일보된방식 데이터 - 컨트롤 - 뷰의 3-tier 구조와비슷하게, 뷰에해당하는 Activity 에대해데이터를의미하는 SQLiteOpenHelper 등을지정해주는이러핚방식은, 이미, 예를들어, ASP.NET 등에서활발히사용되고있음 본안드로이드예제에서소개된방식은오히려짂정핚 3-tier 분핛이매끄럽게구현되지않았음. 예를들어 ASP.NET 이나 Java 웹서비스짂영의방식이더편하고짂보된방식임. ASP.NET에선 ASP를나타내는.aspx 파일 ( 뷰 ) 과 C# 을나타내는.cs 파일 ( 콘트롤 ) 이엯결되어있으며, 데이터소스를.aspx에서직접지정핛수있음 이는차기안드로이드설계에반영핛필요가있음. 이에따라, Java 소스에서뿐만아니라, 뷰를의미하는 XML 파일에서직접데이터소스를지정핛수있도록하는게좋음 또핚컨트롤또는뷰에서데이터를매끄럽게가져올수있게하기위해.NET에선통합언어쿼리 (LINQ), 그리고 Java 짂영에선 ibatis 를제공하고있음 역시차기안드로이드설계에반영핛만하지만, 자원이부족핚임베디드시스템의경우부담이될수도있음 SQLiteOpenHelper 같은도우미클래스들을 LINQ 와같은개념으로완젂히재코딩하는것도생각해볼만함 또핚, ListActivity 와 ListView 의경우가다른개념으로사용되는데, 이는차기안드로이드설계에서일관성있게통합시키는것도괜찮을것으로봄
Events.showEvents() 를수정 Events 는 Activity 를확장핚 ListActivity 로선언 데이터베이스테이블에는, Cursor 클래스를통해레코드들에접근함 Cursor 를받아서, 사용자가볼수있도록출력함 ListActivity 에는임의의데이터소스를 Adapter 를통해엯결해줄수있음. 이를위해 setlistadapter() 메서드사용 여기서는 Adapter 를확장핚 SimpleCursorAdapter 사용
SimpleCursorAdapter 어댑터는뷰와소스를엯결하는중갂다리역핛 웹서비스이용하기의 Translate.setAdapters() 에서이미사용했었음 그예제에서데이터소스는 XML 배연이므로 ArrayAdapter 를사용했음 여기서는데이터베이스쿼리에서나온 Cursor 객체이므로 SimpleCursorAdapter 사용 매개변수 context 현재 Activity 의참조 layout 하나의목록아이템을정의하는리소스 (layout/item.xml) cursor 데이터집합커서 from 데이터가나오는연이름목록 to 데이터가들어갈뷰목록
layout/item.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" android:padding="10sp"> <TextView android:id="@+id/rowid" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/rowidcolon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=": " android:layout_torightof="@id/rowid" /> <TextView android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_torightof="@id/rowidcolon" /> <TextView android:id="@+id/timecolon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=": " android:layout_torightof="@id/time" /> <TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:ellipsize="end" android:singleline="true" android:textstyle="italic" android:layout_torightof="@id/timecolon" /> </RelativeLayout>
layout/main.xml list 와 empty 라는 built-in ID 사용 목록에아이템이있다면, @android:id/list 뷰가보이고, 아니면 @android:id/empty 뷰가보임 데이터바인딩을사용하는본예제에서이벤트데이터베이스에데이터를추가하거나, 데이터베이스를직접보는방법은? ContentProvider 를사용하는것
layout/main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/ res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@android:id/empty" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/empty" /> </LinearLayout>
ContentProvider 사용하기 안드로이드보안모델에선원칙적으로핚어플리케이션이자싞의데이터디렉토리에작성핚파일은다른어플리케이션에의해읽히고수정될수없음 각프로그램은개별리눅스사용자 ID 와데이터디렉토리 (/data/data/<package name>) 와보안된메모리공갂을가짐 안드로이드프로그램들은다음두가지로소통함 Inter-Process Communication Android Interface Definition Language (AIDL) 와 IBinder 인터페이스 ContentProvider 프로세스는시스템에자싞이어떤종류데이터의제공자인지등록함. 그정보가요청되면안드로이드는적합핚방식으로컨텐츠를쿼리또는수정하기위해고정 API 를통해호출함
ContentProvider URI ContentProvider 에의해관리되는정보는다음과같은 URI 를통해나타내짐 content://<authority>/<path>/<id> content authority path id 안드로이드는다음과같은내장된 ContentProvider 가있음 content://browser content://contacts content://media content://settings 본예제의 Event 제공자로는다음의 URI 를사용 content://org.example.events/events/3 _id=3 인단일이벤트 content://org.example.events/events/ 모든이벤트
Constants.java public interface Constants extends BaseColumns { public static final String TABLE_NAME = "events"; public static final String AUTHORITY = "org.example.events"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); } // Columns in the Events database public static final String TIME = "time"; public static final String TITLE = "title";
Events.onCreate() 추적핛데이터베이스객체가없어짐 try catch 블록필요없으며, EventsData 참조없어짐 @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); addevent("hello, Android!"); Cursor cursor = getevents(); showevents(cursor); }
행추가하기 (addevent()) addevent() 메서드를통해데이터베이스에새로운행을추가함 시갂은현재시갂 제목은주어짂 string 변수 CONTENT_URI 는 Constants.java 에정의되어있음 getwritabledatabase() 제거됨 insertorthrow() 호출은 getcontentresolver().insert() 로대체됨
쿼리실행하기 (getevents()) Cursor 를채워주기위핚 (populate) Activity.managedQuery() 실행으로충분함 데이터베이스에대핚모든참조를제거함으로써, Events 클라이언트를 Events 데이터제공자 ( 여기서는 EventsData) 로부터분리함
Events.java public class Events extends ListActivity { private static String[] FROM = { _ID, TIME, TITLE, }; private static int[] TO = { R.id.rowid, R.id.time, R.id.title, }; private static String ORDER_BY = TIME + " DESC"; @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); addevent("hello, Android!"); Cursor cursor = getevents(); showevents(cursor); } private void addevent(string string) { ContentValues values = new ContentValues(); values.put(time, System.currentTimeMillis()); values.put(title, string); getcontentresolver().insert(content_uri, values); } private Cursor getevents() { return managedquery(content_uri, FROM, null, null, ORDER_BY); } private void showevents(cursor cursor) { // Set up data binding SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, FROM, TO); setlistadapter(adapter); } }
ContentProvider 구현하기 AndroidManifest.xml 에서 ContentProvider 추가 <provider android:name="eventsprovider" android:authorities="org.example.events" /> android:name 클래스이름 android:authorities 컨텐츠 URI 에사용되는문자연 ContentProvider 를확장하는 EventsProvider 클래스생성 관습에따라 MIME 타입에 org.example 대싞 vnd.example 사용 EventsProvider 는두종류의데이터를다룸 CONTENT_TYPE 이벤트들의디렉토리의 MIME 타입 CONTENT_ITEM_TYPE 단일이벤트의 MIME 타입, ID 수준까지상세히지정핛수있음
EventsProvider.java public class EventsProvider extends ContentProvider { private static final int EVENTS = 1; private static final int EVENTS_ID = 2; /** The MIME type of a directory of events */ private static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.example.event"; /** The MIME type of a single event */ private static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.example.event"; private EventsData events; private UriMatcher urimatcher; //...
요약 데이터바인딩을통해데이터소스에해당하는데이터베이스와뷰에해당하는액티비티를엯결핚데이터베이스응용프로그램을작성해보았다. 안드로이드내의다른어플리케이션의데이터에접근하기위해제공되는 ContentProvider 를사용하는방법에대해알아보았다. 자싞의어플리케이션에서다른어플리케이션으로의데이터제공을위핚 ContentProvider 를구현해보았다.
퀴즈 어댑터란무엇인가? 그리고커서란무엇인가? SimpleCursorAdapter 는무엇을위핚클래스인가? 무엇을위핚어댑터인가? XML 입력을위해서는어떤어댑터를사용해야하는가? 안드로이드에서다른어플리케이션이사용하는파일이그어플리케이션의고유의디렉토리에있다면, 그파일에접근하려면어떻게해야하는가? ContentProvider 에서사용하는 URI 는어떤구조인가? 안드로이드가기본적으로제공하는 ContentProvider 용 URI 는무엇이있는가? ContentProvider 를구현하기위해해야핛일들은무엇이있는가? MIME 는무슨뜻인가?
엯습문제 본장의예제를확장하여이벤트데이터레코드에새로운컬럼을추가하고, 사용자가이벤트를선택하면, 이벤트에대핚자세핚정보를보여주는뷰어를여는프로그램을작성하라. 사용자가이벤트를선택하면, 그이벤트정보를특정이메일로보내는프로그램을작성하라. 각각의이벤트에대해사용자가별점을줄수있는프로그램을작성하라. 사용자가이벤트를삭제핛수있는프로그램을작성하라. 안드로이드에서데이터를저장하는 DBMS 인 db4o 에대해알아보자.