헬로, 안드로이드 13 주차 SQL 활용하기 (2) 강대기동서대학교컴퓨터정보공학부
학습목표 데이터바인딩을통해데이터소스에해당하는데이터베이스와뷰에해당하는액티비티를연결한데이터베이스응용프로그램을작성할수있다. 안드로이드내의다른어플리케이션의데이터에접근하기위해제공되는 ContentProvider 를사용할수있다. 자신의어플리케이션에서다른어플리케이션으로의데이터제공을위한 ContentProvider 를구현할수있다.
차례 데이터바인딩 ContentProvider 사용하기 ContentProvider 구현하기 요약 퀴즈 연습문제
데이터바인딩 몇줄의코드로데이터 ( 또는모델 ) 과뷰를연결 모델은상황에따라다르게해석되나, ( 예를들어패션모델, 모델하우스, 성능평가모델, 기계학습모델등 ) 데이터베이스에서는데이터모델, 즉스키마와비슷하게해석됨 데이터바인딩을위해, Events 예제를수정하여데이터베이스쿼리결과에엮인 ListView 를사용하도록함
Cursor-Adapter-Activity 간의연결에대 한개인적인견해 ASP.NET 이나 C#.NET 의데이터베이스응용을보면, DataTable 방식과 DataSet 방식이있는데, 지난시간의예제코드는 DataTable 방식에가깝고, 이번시간의 ContentProvider 를사용하는예제코드는 DataSet 방식에가까움 데이터 - 컨트롤 - 뷰의 3-tier 구조와비슷하게, 뷰에해당하는 Activity 에대해데이터를의미하는 Cursor 를지정해주는이러한방식은, 이미, 예를들어, ASP.NET 등에서활발히사용되고있음 본안드로이드예제에서소개된방식은오히려진정한 3-tier 분할이안되고있음. ASP.NET 의방식이더편하고진보된방식임. ASP.NET 에선 ASP 를나타내는.aspx 파일 ( 뷰 ) 과 C# 을나타내는.cs 파일 ( 콘트롤 ) 이연결되어있으며, 데이터소스를.aspx 에서직접지정할수있음 차기안드로이드설계에반영할필요가있음 또한, 차기안드로이드설계에서는, 이런경우 Activity 를확장해서 ListActivity 로사용하는것보다 View 를확장해서 ListView 를사용하게하는게더이치에맞을수도있음. ( 그러나 ListView 는이미가로방향스크롤되는뷰로사용되고있는문제가있기는함 ) 이에따라, Java 소스에서뿐만아니라, 뷰를의미하는 XML 파일에서직접데이터소스를지정할수있도록하는게좋음
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";
Event.onCreate() 추적할데이터베이스객체가없어짐 try catch 블록필요없으며, EventData 참조없어짐 @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 데이터제공자로부터분리함
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 는무슨뜻인가?
연습문제 본장의예제를확장하여이벤트데이터레코드에새로운컬럼을추가하고, 사용자가이벤트를선택하면, 이벤트에대한자세한정보를보여주는뷰어를여는프로그램을작성하라. 사용자가이벤트를선택하면, 그이벤트정보를특정이메일로보내는프로그램을작성하라. 각각의이벤트에대해사용자가별점을줄수있는프로그램일작성하라. 사용자가이벤트를삭제할수있는프로그램을작성하라. 안드로이드에서데이터를저장하는또다른방법인 db4o 에대해알아보자.