apache 인스톨
yum install httpd

설정파일은 아래 디렉토리에 들어있게 됨
/etc/httpd

서버의 시작 종료
/etc/init.d/httpd start/stop

초기웰컴화면은 아래에 위치함
/var/www/error

초기 웰컴하면 설정파일은 아래에 있음
/etc/httpd/conf.d/welcome.conf




신고

'알짜정보 > Linux server' 카테고리의 다른 글

cent os php 설치  (35) 2011.01.02
cent os mysql 설치  (30) 2011.01.02
cent os apache 설치  (28) 2011.01.02
cent os java 설치  (56) 2011.01.02
cent os 기본정보 확인  (24) 2010.12.30
리눅스 설치기  (29) 2010.03.17
by cranix 2011.01.02 18:49


java 같은것은 기본 설치 경로에 설치하는것이 좋다고 판단되어 rpm 버젼으로 받아서 설치합니다.

rpm 버젼은 오라클에 있는 공식 자바 사이트가서 다운로드 하면됩니다.

참고로 링크를 wget 로 받아도 다운로드 됩니다.

그럼 먼저 권한을 변경하고

chmod 755 jdk-6u23-linux-x64-rpm.bin

실행합니다.

./jdk-6u23-linux-x64-rpm.bin

설치가 끝나면 아래와같이 자바 버젼을 확인합니다.
java -version
만약 자신이 설치한 버젼이 나오지 않는다면 아래 명령어로 현재 사용하는 java 를 등록해 줘야합니다.
alternatives --install /usr/bin/java java /usr/java/jdk1.6.0_23/bin/java 1
alternatives --config java



위 버젼의 RPM으로 설치하면 기본적으로 디렉토리는 아래에 설치됩니다.
/usr/java/jdk1.6.0_23
그리고 편의를 위해 아래와같은 심볼릭 링크가 걸리게 됩니다.
/usr/java/default -> /usr/java/jdk1.6.0_23
/usr/java/latest -> /usr/java/jdk1.6.0_23
/usr/bin/java -> /usr/java/default/bin/java

신고

'알짜정보 > Linux server' 카테고리의 다른 글

cent os mysql 설치  (30) 2011.01.02
cent os apache 설치  (28) 2011.01.02
cent os java 설치  (56) 2011.01.02
cent os 기본정보 확인  (24) 2010.12.30
리눅스 설치기  (29) 2010.03.17
ubuntu 터미널 한글깨질때  (752) 2010.02.01
by cranix 2011.01.02 17:08

1. 버젼확인

cat /etc/redhat-release

 

2. 커널버젼 출력

uname –r

 

3. 운영체제명, 커널버젼, gcc컴파일러버젼, 생성한 날짜등의 정보를 확인가능

cat /proc/version

 

4. 터미널 한글설정

export LANG=ko_KR

export LC_ALL=”ko_KR.eucKR”

 

5. 모든 유저 로그인스크립트

/etc/profile

신고

'알짜정보 > Linux server' 카테고리의 다른 글

cent os apache 설치  (28) 2011.01.02
cent os java 설치  (56) 2011.01.02
cent os 기본정보 확인  (24) 2010.12.30
리눅스 설치기  (29) 2010.03.17
ubuntu 터미널 한글깨질때  (752) 2010.02.01
ubuntu 오픈오피스 글자 깨질때  (787) 2010.02.01
by cranix 2010.12.30 18:28

안드로이드의 연락처 데이터베이스 구조는 상당히 복잡한 구조로 되어있습니다.

연락처 관련 어플을 만들기 위해서는 이러한 구조를 이해해야지만 데이터를 관리하는것이 가능합니다.

이번 포스트에서는 안드로이드에서 연락처데이터를 어떻게 관리하는지를 알아보도록 하겠습니다.

 

그럼 먼저 안드로이드 개발자사이트에 있는 연락처 데이터베이스의 구조 그림을 보도록 합시다.

 

contacts-2

 

저는 처음에 이 그림을 보고 “데이터베이스를 사용하지 않았나?” 라는 생각을 했습니다.

그러나 안드로이드 연락처의 기본 데이터는 sqllite 데이터베이스를 사용합니다.

 

사실 이것만 봐서는 감이 잘 안오는데 데이터베이스를 까보면 확실해 집니다.

그럼 안드로이드 연락처 데이터베이스 파일을 열어서 테이블 구조를 확인해 보도록 하겠습니다.

 

 

 

- 연락처 데이터베이스 구조

먼저 sqllite 데이터베이스를 좀 더 쉽게보기 위해서 아래 사이트에서 툴을 다운로드 하세요.

http://sqliteadmin.orbmu2k.de/

 

그다음 안드로이드의 DDMS 를 켠다음 에뮬레이터의 아래와같은 파일을 다운로드 받습니다.

/data/data/com.android.providers.contacts/databases/contacts2.db

 

이것이 바로 안드로이드의 연락처 sqllite 데이터베이스 파일이며 sqllite 어드민툴을 가지고 열어보면 아래와 같은 형태로 구조를 확인할 수 있습니다.

image

 

 

테이블은 여러가지가 있지만 여기서 중요하게 볼 것은 contacts, raw_contacts, data 테이블 입니다.

이 세 테이블에 연락처의 기본 데이터가 저장되게 됩니다.

 

 

 

 

- 접근방식

사실 sqllite 는 데이터베이스 파일에 접근 권한만 있다면 테이블의 모든 내용을 수정/삭제/업데이트 할 수 있습니다.

그래서 이러한 연락처 테이블은 각 어플에서 바로 접근 가능한 것이 아니라 Contacts provider 를 통해서 접근하게 됩니다.

 

image

 

위와 같이 어플들은 sqllite 데이터베이스 파일인 contacts2.db 파일에 바로 접근하지 못하고 provider 를 통해서만 접근할 수 있습니다.

사용자가 연락처 데이터베이스를 업데이트 하려고 요청을 하면 데이터베이스에 접근하기 전에 Contacts provider 가 먼저 접근권한을 판단해 줍니다.

 

참고로 안드로이드는 android.provider.ContactsContract 클래스의 하위 클래스로 테이블명과 동일한 이름의 클래스를 지원합니다.

예를들어 android.provider.ContactsContract.Contacts 클래스는 contacts 테이블과 매칭되며 다양한 접근 정보를 알 수 있습니다.

 

 

 

- 연락처 업데이트

안드로이드의 연락처 구조가 위와같은 형태라면 연락처를 추가하기 위해서는 가장먼저 contacts 테이블에 추가해야 하는것 처럼 보입니다.

그러나 안드로이드 에서는 contacts 테이블에 바로 연락처를 추가하는것을 제공하지 않습니다.

provider 에서 insert 를 제공하는 테이블은 raw_contacts 와 data 입니다.

 

그렇다면 어떻게 contacts 테이블에 연락처를 추가 할 수 있을까요?

바로 raw_contacts 테이블에 데이터를 추가하면 자동으로 contacts 테이블에 추가해 줍니다.

이것을 안드로이드에서는 “Auto aggregation” 이라고 부릅니다.

 

안드로이드는 “Auto aggreation” 을 수행하면서 contacts 테이블에 데이터를 추가할지 아니면 업데이트할지를 결정합니다.

만약 추가된 raw_contacts 를 기존의 contacts 에 업데이트 하려면 아래와 같은 조건을 검사합니다.

    - 이름이 같을때

    - 이름이 동일한 단어로 구성되어있지만 순서가 다를때

    - 전화번호나, 이메일 아니면 닉네임을 공유할때

 

이러한 조건에 의해 contacts 테이블이 자동으로 업데이트 됩니다.

 

연락처를 추가하기 위해서는 아래와 같은 코드를 사용합니다.

ContentValues values = new ContentValues();
values.put(RawContacts.SOURCE_ID, sourceId);
values.put(RawContacts.ACCOUNT_NAME, account.name);
values.put(RawContacts.ACCOUNT_TYPE, account.type);
Uri uri = resolver.insert(RawContacts.CONTENT_URI, values);

SOURCE_ID 는 해당 계정에서 유일한 계정의 id 를 말합니다.

 

 

- 연락처 삭제

데이터베이스를 까보면 테이블의 삭제에 trigger 가 걸려있는것을 볼 수 있습니다.

그래서 삭제시 연관된 다른 테이블의 데이터를 같이 삭제하게 됩니다.

 

예를들어 raw_contacts 를 삭제하면 종속된 data 는 자동으로 삭제됩니다.

contacts 를 지우면 거기에 종속된 raw_contacts 역시 자동으로 삭제됩니다.

 

그러나 안드로이드에서 contacts provider 를 이용해서 raw_contacts 를 삭제하면 데이터베이스 에서 바로 지워지지 않는것을 알 수 있습니다.

이것은 실제 삭제는 하지않고 raw_contacts 테이블의 deleted 필드를 1로 셋팅합니다.

 

이렇게 셋팅된 데이터는 SyncAdapter 에 의해서 실제로 삭제가 이루어지게 됩니다.

그렇다면 SyncAdapter 에서는 어떻게 실 삭제를 할까요?

그것은 아래와 같은 코드로 이루어지게 됩니다.

resolver.delete(
RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build(),
RawContacts.ACCOUNT_NAME +"=? AND " + RawContacts.ACCOUNT_TYPE+"=? AND "+RawContacts.DELETED+"=1",
new String[]{account.name,account.type}
);

즉 실제로 삭제를 하기위해서는 contacts provider 에 CALLER_IS_SYNCADAPTER 파라메터에 “true” 를 심어서 넘겨주면 됩니다.

 

 

 

 

- 데이터 추가

데이터는 이름, 전화번호, 이메일 등 연락처에 표시하는 모든 내용을 저장 하는 테이블 입니다.

데이터는 raw_contacts 에 종속된 테이블 입니다.

그래서 데이터를 생성할 때에는 RAW_CONTACT_ID 를 꼭 써 주어야 합니다.

 

예를들어 아래와 같은 형태로 데이터를 생성합니다.

ContentValues values = new ContentValues();
values.put(Phone.RAW_CONTACT_ID,rawId);
values.put(Phone.NUMBER, phone);
values.put(Phone.TYPE, phoneType);
values.put(Phone.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

 

데이터는 MIMETYPE 에 따라 구조가 모두 다릅니다.

위의 예제는 전화번호를 등록하는 과정이며 Phone.CONTENT_ITEM_TYPE 에 정의되어있는 String 형태의 mime type 을 같이 넘기는것을 볼 수 있습니다.

 

그럼 이름데이터를 저장하는 예제를 보면 아래와 같습니다.

ContentValues values = new ContentValues();
values.put(StructuredName.RAW_CONTACT_ID, rawId);
values.put(StructuredName.GIVEN_NAME, firstName);
values.put(StructuredName.FAMILY_NAME, lastName);
values.put(StructuredName.DISPLAY_NAME, firstName + " " + lastName);
values.put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

이것역시 RAW_CONTACT_ID 와 MIMETYPE 을 저장하지만 나머지는 모두 달라진것을 볼수 있습니다.

 

이러한 가변적인 데이터 구조중 많이 쓰는 데이터구조를 모아서 API 로 만들어 놨는데 예를들어 전화번호, 이름, 이메일 등이 있습니다.

이것은 “android.provider.ContactsContact.CommonDataKinds” 클래스의 하위클래스로서 들어있습니다.

위의 예제들 역시 이 클래스의 하위클래스인 Phone 과 StructuredName 을 이용해서 만들었습니다.

 

이처럼 안드로이드에서는 가변적인 DATA 의 구조를 택함으로써 무한에 가까운 필드확장을 할 수 있게 만들어 놨습니다.

신고
by cranix 2010.12.27 17:01

안드로이드의 연락처 데이터베이스 구조는 상당히 복잡한 구조로 되어있습니다.

연락처 관련 어플을 만들기 위해서는 이러한 구조를 이해해야지만 데이터를 관리하는것이 가능합니다.

이번 포스트에서는 안드로이드에서 연락처데이터를 어떻게 관리하는지를 알아보도록 하겠습니다.

 

그럼 먼저 안드로이드 개발자사이트에 있는 연락처 데이터베이스의 구조 그림을 보도록 합시다.

 

contacts-2

 

저는 처음에 이 그림을 보고 “데이터베이스를 사용하지 않았나?” 라는 생각을 했습니다.

그러나 안드로이드 연락처의 기본 데이터는 sqllite 데이터베이스를 사용합니다.

 

사실 이것만 봐서는 감이 잘 안오는데 데이터베이스를 까보면 확실해 집니다.

그럼 안드로이드 연락처 데이터베이스 파일을 열어서 테이블 구조를 확인해 보도록 하겠습니다.

 

 

 

- 연락처 데이터베이스 구조

먼저 sqllite 데이터베이스를 좀 더 쉽게보기 위해서 아래 사이트에서 툴을 다운로드 하세요.

http://sqliteadmin.orbmu2k.de/

 

그다음 안드로이드의 DDMS 를 켠다음 에뮬레이터의 아래와같은 파일을 다운로드 받습니다.

/data/data/com.android.providers.contacts/databases/contacts2.db

 

이것이 바로 안드로이드의 연락처 sqllite 데이터베이스 파일이며 sqllite 어드민툴을 가지고 열어보면 아래와 같은 형태로 구조를 확인할 수 있습니다.

image

 

 

테이블은 여러가지가 있지만 여기서 중요하게 볼 것은 contacts, raw_contacts, data 테이블 입니다.

이 세 테이블에 연락처의 기본 데이터가 저장되게 됩니다.

 

 

 

 

- 접근방식

사실 sqllite 는 데이터베이스 파일에 접근 권한만 있다면 테이블의 모든 내용을 수정/삭제/업데이트 할 수 있습니다.

그래서 이러한 연락처 테이블은 각 어플에서 바로 접근 가능한 것이 아니라 Contacts provider 를 통해서 접근하게 됩니다.

 

image

 

위와 같이 어플들은 sqllite 데이터베이스 파일인 contacts2.db 파일에 바로 접근하지 못하고 provider 를 통해서만 접근할 수 있습니다.

사용자가 연락처 데이터베이스를 업데이트 하려고 요청을 하면 데이터베이스에 접근하기 전에 Contacts provider 가 먼저 접근권한을 판단해 줍니다.

 

참고로 안드로이드는 android.provider.ContactsContract 클래스의 하위 클래스로 테이블명과 동일한 이름의 클래스를 지원합니다.

예를들어 android.provider.ContactsContract.Contacts 클래스는 contacts 테이블과 매칭되며 다양한 접근 정보를 알 수 있습니다.

 

 

 

- 연락처 업데이트

안드로이드의 연락처 구조가 위와같은 형태라면 연락처를 추가하기 위해서는 가장먼저 contacts 테이블에 추가해야 하는것 처럼 보입니다.

그러나 안드로이드 에서는 contacts 테이블에 바로 연락처를 추가하는것을 제공하지 않습니다.

provider 에서 insert 를 제공하는 테이블은 raw_contacts 와 data 입니다.

 

그렇다면 어떻게 contacts 테이블에 연락처를 추가 할 수 있을까요?

바로 raw_contacts 테이블에 데이터를 추가하면 자동으로 contacts 테이블에 추가해 줍니다.

이것을 안드로이드에서는 “Auto aggregation” 이라고 부릅니다.

 

안드로이드는 “Auto aggreation” 을 수행하면서 contacts 테이블에 데이터를 추가할지 아니면 업데이트할지를 결정합니다.

만약 추가된 raw_contacts 를 기존의 contacts 에 업데이트 하려면 아래와 같은 조건을 검사합니다.

    - 이름이 같을때

    - 이름이 동일한 단어로 구성되어있지만 순서가 다를때

    - 전화번호나, 이메일 아니면 닉네임을 공유할때

 

이러한 조건에 의해 contacts 테이블이 자동으로 업데이트 됩니다.

 

연락처를 추가하기 위해서는 아래와 같은 코드를 사용합니다.

ContentValues values = new ContentValues();
values.put(RawContacts.SOURCE_ID, sourceId);
values.put(RawContacts.ACCOUNT_NAME, account.name);
values.put(RawContacts.ACCOUNT_TYPE, account.type);
Uri uri = resolver.insert(RawContacts.CONTENT_URI, values);

SOURCE_ID 는 해당 계정에서 유일한 계정의 id 를 말합니다.

 

 

- 연락처 삭제

데이터베이스를 까보면 테이블의 삭제에 trigger 가 걸려있는것을 볼 수 있습니다.

그래서 삭제시 연관된 다른 테이블의 데이터를 같이 삭제하게 됩니다.

 

예를들어 raw_contacts 를 삭제하면 종속된 data 는 자동으로 삭제됩니다.

contacts 를 지우면 거기에 종속된 raw_contacts 역시 자동으로 삭제됩니다.

 

그러나 안드로이드에서 contacts provider 를 이용해서 raw_contacts 를 삭제하면 데이터베이스 에서 바로 지워지지 않는것을 알 수 있습니다.

이것은 실제 삭제는 하지않고 raw_contacts 테이블의 deleted 필드를 1로 셋팅합니다.

 

이렇게 셋팅된 데이터는 SyncAdapter 에 의해서 실제로 삭제가 이루어지게 됩니다.

그렇다면 SyncAdapter 에서는 어떻게 실 삭제를 할까요?

그것은 아래와 같은 코드로 이루어지게 됩니다.

resolver.delete(
RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build(),
RawContacts.ACCOUNT_NAME +"=? AND " + RawContacts.ACCOUNT_TYPE+"=? AND "+RawContacts.DELETED+"=1",
new String[]{account.name,account.type}
);

즉 실제로 삭제를 하기위해서는 contacts provider 에 CALLER_IS_SYNCADAPTER 파라메터에 “true” 를 심어서 넘겨주면 됩니다.

 

 

 

 

- 데이터 추가

데이터는 이름, 전화번호, 이메일 등 연락처에 표시하는 모든 내용을 저장 하는 테이블 입니다.

데이터는 raw_contacts 에 종속된 테이블 입니다.

그래서 데이터를 생성할 때에는 RAW_CONTACT_ID 를 꼭 써 주어야 합니다.

 

예를들어 아래와 같은 형태로 데이터를 생성합니다.

ContentValues values = new ContentValues();
values.put(Phone.RAW_CONTACT_ID,rawId);
values.put(Phone.NUMBER, phone);
values.put(Phone.TYPE, phoneType);
values.put(Phone.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

 

데이터는 MIMETYPE 에 따라 구조가 모두 다릅니다.

위의 예제는 전화번호를 등록하는 과정이며 Phone.CONTENT_ITEM_TYPE 에 정의되어있는 String 형태의 mime type 을 같이 넘기는것을 볼 수 있습니다.

 

그럼 이름데이터를 저장하는 예제를 보면 아래와 같습니다.

ContentValues values = new ContentValues();
values.put(StructuredName.RAW_CONTACT_ID, rawId);
values.put(StructuredName.GIVEN_NAME, firstName);
values.put(StructuredName.FAMILY_NAME, lastName);
values.put(StructuredName.DISPLAY_NAME, firstName + " " + lastName);
values.put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

이것역시 RAW_CONTACT_ID 와 MIMETYPE 을 저장하지만 나머지는 모두 달라진것을 볼수 있습니다.

 

이러한 가변적인 데이터 구조중 많이 쓰는 데이터구조를 모아서 API 로 만들어 놨는데 예를들어 전화번호, 이름, 이메일 등이 있습니다.

이것은 “android.provider.ContactsContact.CommonDataKinds” 클래스의 하위클래스로서 들어있습니다.

위의 예제들 역시 이 클래스의 하위클래스인 Phone 과 StructuredName 을 이용해서 만들었습니다.

 

이처럼 안드로이드에서는 가변적인 DATA 의 구조를 택함으로써 무한에 가까운 필드확장을 할 수 있게 만들어 놨습니다.

신고
by cranix 2010.12.27 17:01

이전에 Authenticator Service 를 구현해 보았습니다.

이제 이것을 이용하는 SyncAdapter 를 구현해 보도록 하겠습니다.

 

SyncAdapter 는 구글 싱크가 일어날때 자동으로 싱크를 처리해주는 부분으로서

“Accounts & Sync” 셋팅메뉴에서 원하는 아이디를 클릭하면 SyncAdapter 의 리스트를 확인할 수 있습니다.

 

그럼 먼저 “Accounts & Sync” 메뉴에서 “Add account” 버튼을 눌러 이전에 만들었던 계정으로 로그인을 합니다.

 

image

 

위와 같이 로그인된 아이디가 저장이 될텐데 저 아이디를 클릭하면 이 계정에 등록되어진 SyncAdapter 를 확인할 수 있습니다.

현재에는 하나도 없는것을 볼 수 있는데 이번 포스트에서 여기에 추가해보도록 하겠습니다.

 

SyncAdapter 는 Service 로 이루어져 있습니다.

 

Service 를 추가하기전에 SyncAdapter 를 쓰기 위한 권한을 추가합니다.

아래 내용을 manifest 에 등록합니다.

 

<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />  
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

이제 아래와 같은 형태의 서비스를 추가합니다.

<service android:name=".syncadapter.SyncService">
<intent-filter>
<action
android:name="android.content.SyncAdapter"
/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>

그다음 syncadapter.xml 파일을 추가합니다.

<?xml version="1.0" encoding="utf-8"?>

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="net.cranix.android.cranixsyncsample"
android:supportsUploading="false"
/>

“android:contentAuthority” 는 sync 할 ContentProvider 를 지정합니다.

여기서 Sync 이름은 우리가 직접 지정하는 것이 아니고 이 ContentProvider 의 이름을 따라가기 때문에 유의해야 합니다.

 

“android:supportsUploading” 은 폰에서 이 SyncAdapter 의 데이터를 변경 가능한게 할지 말지를 결정하는 것 입니다.

이것을 false 로 하면 서버에서 싱크만 되고 폰에서는 수정이안되는 readonly 상태가 됩니다.

 

이제 마지막으로 SyncService 와 SyncAdapter 소스를 구현해 보도록 합니다.

먼저 SyncService 입니다.

package net.cranix.android.cranixsyncsample.syncadapter;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class SyncService extends Service {
private SyncAdapter syncAdapter;

@Override
public void onCreate() {
super.onCreate();
syncAdapter = new SyncAdapter(this,true);
}
@Override
public IBinder onBind(Intent arg0) {
return syncAdapter.getSyncAdapterBinder();
}

}


이 Service 는 SyncAdapter 에 모든 처리를 위임합니다.

SyncAdapter 를 보면 다음과 같습니다.

package net.cranix.android.cranixsyncsample.syncadapter;



import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;

public class SyncAdapter extends AbstractThreadedSyncAdapter {



public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}

@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {

// TODO : Sync 처리
}
}

 

이제 “Accounts & sync” 메뉴에서 아이디를 클릭해 보면 아래와 같이 추가된 SyncAdapter 가 보일 것입니다.

image

 

 

이제 위의 “Sync Contacts” 를 클릭하거나 시스템에서 sync 가 발생할때 위 SyncAdapter 의 onPerformSync 메소드가 호출되게 됩니다.

결국 실제 Sync 를 처리하는 구문은 onPerformSync 메소드만 구현해주면 됩니다.

신고
by cranix 2010.12.27 14:08

전 포스트에서 AbstractAccountAuthenticator 의 addAccount 를 구현해서 아이디를 추가하는것을 해 보았습니다.

그런데 실제로 서비스에서는 “Add account” 를 누르면 아이디 비밀번호를 묻는 UI 가 나와야 정상일 것입니다.

그래서 안드로이드는 이를위해 AccountAuthenticatorActivity 를 제공합니다.

 

여기서 쓰는 UI 는 아이디와 비밀번호를 입력받는 EditText 가 두개있고 login 과 cancel 버튼이 있는 레이아웃 입니다.

여기서 쓰는 xml 은 아래와 같습니다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ScrollView android:layout_height="wrap_content"
android:id="@+id/ScrollView01"
android:layout_weight="1"
android:layout_width="fill_parent">

<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">

<EditText android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/EditText_id"
android:hint="아이디">
</EditText>
<EditText android:inputType="textPassword"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/EditText_password"
android:hint="비밀번호">
</EditText>
</LinearLayout>
</ScrollView>

<LinearLayout android:id="@+id/LinearLayout02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#c6c3c6">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Button_login"
android:text="Login">
</Button>
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Button_cancel"
android:text="Cancel">
</Button>
</LinearLayout>
</LinearLayout>

 

매니페스트에는 아래와 같이 activity 를 추가합니다.

<activity android:name=".authenticator.AuthenticatorActivity" 
android:theme="@android:style/Theme.Dialog">
</activity>

 

이제 AuthenticatorActivity 소스를 보면 아래와 같습니다.

package net.cranix.android.cranixsyncsample.authenticator;

import net.cranix.android.cranixsyncsample.Constants;
import net.cranix.android.cranixsyncsample.R;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class AuthenticatorActivity extends AccountAuthenticatorActivity {
private EditText textId;
private EditText textPassword;

private AccountManager accountManager;

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

final Button btnLogin = (Button) findViewById(R.id.Button_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doLogin();
}
});

final Button btnCancel = (Button) findViewById(R.id.Button_cancel);
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doCancel();
}
});

textId = (EditText) findViewById(R.id.EditText_id);
textPassword = (EditText) findViewById(R.id.EditText_password);
accountManager = AccountManager.get(this);
}

private void doLogin() {
final String id = textId.getText().toString();
final String password = textPassword.getText().toString();
final Account account = new Account(id,Constants.ACCOUNT_TYPE);
if (accountManager.addAccountExplicitly(account, password, null)) {
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, id);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);

setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK,intent);
finish();
}
else {
doCancel();
}
}
private void doCancel() {
setResult(RESULT_CANCELED);
finish();
}
}

 

AccountAuthenticatorActivity 는 일반 Activity 와 다른점이 두가지 있습니다.

   1. 액티비티를 띄울때 intent 의 extra 로 AccountAuthenticatorResponse 객체를 담고 있어야 함.

   2. “setAccountAuthenticatorResult(bundle)” 메소드로 결과를 돌려줘야함.

 

위의 doLogin 함수에서 setAccountAuthenticatorResult 메소드를 쓰는것을 볼 수 있습니다.

이제 이 Activity 를 띄우기 위해서 지난번에 만들었던 Authenticator 클래스를 수정해 봅시다.

 

public class Authenticator extends AbstractAccountAuthenticator {
private Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {

Intent intent = new Intent(context,AuthenticatorActivity.class);

intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}

...
...
}

 

여기서 intent 의 extra 로 response 를 넣어서 만든후에 bundle 에 담아서 넘겨주는것을 볼 수 있습니다.

이렇게 하면 자동으로 인텐트에 지정된 Activity 가 뜨게 되면서 처리를 위임하게 됩니다.

 

이제 “Add account” 누른후 자신의 계정을 클릭하면 아래와 같은 화면이 나오는 것을 볼 수 있습니다.

image

 

 

이제 껍데기는 다 만들었습니다.

이제 이 아이디를 이용해서 sync 를 구현하면 됩니다.

sync 구현 역시 새로운 서비스를 만들어야 하는데 이는 다음번에 알아보도록 하겠습니다.

신고
by cranix 2010.12.24 15:03

안드로이드에서 final 을 자주 쓰는이유가 무엇일까요?

 

먼저 코드를 봅시다.

 

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ButtonTestActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Button btn = (Button) findViewById(R.id.Button01);

btn.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
btn.setText("clicked!");
}
});
}
}

 

여기서 final 을 쓴 이유가 무엇일까요?

저기서 final 을 쓰지 않으면 아래와 같은 오류가 납니다.

 

Cannot refer to a non-final variable btn inside an inner class defined in a different method

 

사실 처음에는 “final 을 쓰면 전역변수처럼 되나?” 라고 생각했는데 그렇다면 static 하고 비교가 애매해 집니다.

그리고 메모리관리 측면에서도 낭비가 되죠.

 

여기서 final 의 일반적인 정의를 좀 알아봅시다.

바로 “상수화” 시킨다는것 입니다.

 

사실 이 말은 프리미티브 타입에만 통하는 말입니다.

프리미티브 타입이 아닌 객체에는 “참조를 변경할수 없음” 이라는 말이 더 어울립니다.

 

결국 위의 소스에서 btn 이라는 변수의 참조가 변경되지 않기 때문에 컴파일러가 변수명이 아니라 참조주소로 바로 접근 가능하도록 할 수 있게 되는것 입니다.

 

이것은 전역변수는 아니고 “지역적 치환문” 정도로 생각하는게 좋을꺼 같습니다.

신고
by cranix 2010.12.23 18:32

이번에는 AuthenticationService 를 실제로 구현해 보도록 하겠습니다.

 

이전에 “Accounts & Sync” 메뉴에 내 계정을 등록시키는 작업을 했습니다.

그 다음에 원하는 것은 내가 만든 계정을 클릭했을 때 로그인 창을 띄우는 것 입니다.

 

그런데 여기서 문제는 실제로 로그인 을 요청하는 부분은 다른 Process 가 한다는 것이죠.

결국 이 프로세스가 우리가 만든 Service 와 통신하기 위해서는 바인더를 이용하여야 합니다.

 

안드로이드에서는 이를 위해서 AbstractAccountAuthenticator 라는 추상 클래스를 제공합니다.

우리는 이를 구현해서 iBinder 만 넘겨주면 됩니다.

 

이를 소스로 살펴보면 아래와 같습니다.

 

package net.cranix.android.cranixsyncsample.authenticator;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticationService extends Service {
private Authenticator authenticator;

@Override
public void onCreate() {
authenticator = new Authenticator(this);
}

@Override
public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
}



 

즉 Authenticator 는 AbstractAccountAuthenticator 를 구현한 클래스이고 이것을 우리가 만든 서비스에서 만들고 사용하는 모습입니다.

 

이제 로그인 및 기타 처리는 Authenticator 클래스에서 구현해주면 됩니다.

Authenticator 클래스는 아래와 같습니다.

 

package net.cranix.android.cranixsyncsample.authenticator;

import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;


public class Authenticator extends AbstractAccountAuthenticator {

public Authenticator(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response,
Account account, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response,
String accountType) {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response,
Account account, String[] features) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

}



 

AbstractAccountAuthenticator 를 상속받은 Authenticator 클래스의 기본형은 위와 같습니다.

이제 각각의 메소드들은 시스템에서 계정관련 작업시 알아서 호출하게 됩니다.

 

여기서는 간단하게 계정추가를 만들어보도록 하겠습니다.

계정추가는 설정에 “Accounts & Sync” 에서 계정을 누를때 일어나며, 위의 메소드 중에서는 “addAccount” 를 호출하게 됩니다.

 

그럼 위 소스를 아래와 같이 수정합니다.

 

public class Authenticator extends AbstractAccountAuthenticator { 
private Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {

AccountManager manager = AccountManager.get(context);

Account account = new Account("cranix",accountType);
manager.addAccountExplicitly(account, "passowrd", null);
return new Bundle();
}




}

 

여기서는 AccountManager.addAccountExplicitly 를 사용하여 계정을 추가하는데 이것을 사용하려면 권한이 필요합니다.

아래내용을 manifest 파일에 추가합니다.

 

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"></uses-permission>

 

이제 설정 메뉴의 “Accounts & Sync” 에서 “Add account” 를 누른후에 자신이 추가한 계정을 클릭하면 아래와 같이 추가되는것을 볼 수 있습니다.

image

 

이번 포스트에서는 UI 없이 간단하게 추가되도록 만들었습니다.

안드로이드에서는 여기서 사용하는 UI 를 지원하기 위해서 AccountAuthenticatorActivity 라는 클래스를 제공합니다.

 

다음 에는 이 클래스를 이용해서 UI 를 붙혀서 로그인 하는법을 알아보도록 하겠습니다.

신고
by cranix 2010.12.23 17:21

안드로이드에서 ID/PW 는 어떻게 저장할까요?

어플리케이션 별로 알아서 저장해도 상관은 없지요.

 

그러나 안드로이드에서는 이러한 로그인 정보를 통합적으로 관리 할 수 있도록 Authenticator Service 를 제공합니다.

안드로이드의 설정에 “Accounts & Sync” 메뉴에 가보면 이렇게 관리되는 계정을 확인 할 수 있습니다.

 

아마 대부분 Google 계정만 등록되어 있을텐데 이번 포스트에서는 내가 만든 Authenticator Service 를 등록해 보도록 하겠습니다.

 

 

먼저 설정의 “Accounts & Sync” 메뉴의 계정리스트에 나오도록 해야하는데 이것은 서비스 구현으로 이루어 집니다.

 

1. manifest 에 서비스 등록하기

   - AndroidManifest.xml 파일에 다음과 같이 서비스를 등록합니다.

<service android:name=".authenticator.AuthenticationService"> 
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator"
/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"
/>
</service>


 

2. xml 리소스 등록하기

   - xml/authenticator.xml 파일을 만들고 아래 내용을 입력합니다.

<?xml version="1.0" encoding="utf-8"?>


<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="net.cranix.android.cranixsyncsample"
android:icon="@drawable/icon"
android:smallIcon="@drawable/icon"
android:label="@string/app_name"
/>


 

3. sample 서비스 만들기

   - 일단 등록이 되는지 확인을 위해 아래와같이 빈 Service 를 만듭니다.

package net.cranix.android.cranixsyncsample.authenticator;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticationService extends Service {
@Override
public void onCreate() {
// TODO Auto-generated method stub
}

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}

}


 

   - 그후 설정에 “Accounts & Sync” 에 가보면 등록이 되어 있는것을 볼 수 있습니다.

image

 

 

자신이 추가한 계정을 클릭하면 아무 반응이 없는것을 알 수 있습니다.

서비스를 구현하지 않았으니 당연한 결과 입니다.

 

3번에서 만든 서비스를 구현해서 로그인 정보 기록이나 나아가서 주소록 싱크 어플 등을 만들수 있게 됩니다.

다음에는 이 서비스를 실제로 구현하는 방법을 알아보도록 하겠습니다.

신고
by cranix 2010.12.23 14:53

안드로이드 sync 를 사용하기 위해서는 계정접근을 위한 Service 와 sync 를 위한 Service 가 필요합니다.

 

 

1. 계정 접근을 위한 Service

   - 아래와 같은 형태로 Service 를 추가하게 됩니다.

<service
    android:name=".authenticator.AuthenticationService"
    android:exported="true">
    <intent-filter>
        <action
            android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator" />
</service>

 

   - 위와 같은 형태로 서비스를 등록하면 안드로이드 설정메뉴중 “Accounts & Sync” 메뉴에 자동으로 등록됩니다.image

 

   - 여기 나오는 메뉴의 형태는 @xml/authenticator 에서 정의합니다.

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.example.android.samplesync"
    android:icon="@drawable/icon"
    android:smallIcon="@drawable/icon"
    android:label="@string/label"
/>

 

  - 로그인후 받은 토큰을 내부에 저장하고 다음번 로그인시에 그 토큰으로 자동으로 로그인합니다.

 

 

 

2. Sync 를 위한 Service

  - 아래와 같은 형태로 Service 를 추가하게 됩니다.

<service
    android:name=".syncadapter.SyncService"
    android:exported="true">
    <intent-filter>
        <action
            android:name="android.content.SyncAdapter" />
    </intent-filter>
    <meta-data
        android:name="android.content.SyncAdapter"
        android:resource="@xml/syncadapter" />
    <meta-data
        android:name="android.provider.CONTACTS_STRUCTURE"
        android:resource="@xml/contacts" />
</service>

 

   - 싱크가 실행될때 위의 서비스가 자동으로 호출되게 됩니다.

   - 싱크는 저장된 토큰으로 외부와 통신해서 업데이트된 데이터를 받아와서 내부에 맞게 가공해서 업데이트 하도록 합니다.

신고
by cranix 2010.12.20 11:39
| 1 2 3 4 5 6 7 ··· 10 |