anatomy of realm

65
Anatomy of Realm Leonardo YongUk Kim ([email protected])

Upload: leonardo-yonguk-kim

Post on 11-Apr-2017

646 views

Category:

Technology


4 download

TRANSCRIPT

Anatomy�of�RealmLeonardo�YongUk�Kim�([email protected])

Realm�소개

3분만에�살펴보는�Realm�사용법

이제�Realm을의식(?)의�흐름에�따라

살펴봅시다.

Realm�모델�객체의�특성

• 특정�객체를�상속받거나�구현해야�합니다.�

• RealmObject를�상속�

• RealmModel을�구현�(+@RealmClass)�

• 두가지�상태를�가집니다.�

• managed:�Realm에�관리되는�객체�

• unmanaged:�POJO�객체

왜�이렇게�하시는거죠?

• 아래의�이점을�위해,�하부�엔진과�실시간�연동합니다.�

• 무�복제�메커니즘�

• 크로스플랫폼�호환성�

• 자동�갱신

왜�이렇게�하시는거죠?

• 아래의�이점을�위해,�하부�엔진과�실시간�연동합니다.�

• 무�복제�메커니즘�

• 실제로�사용되기�전에는�Java�세상에�데이터가�복제되지�않습니다.�

• 크로스플랫폼�호환성�

• 자동�갱신

과거�페이스북�앱의�문제점

• JSON�인코딩과�디코딩에�많은�리소스를�허비.�

• 사용되지�않은�데이터도�디코딩을�해서�Java�객체에�담았어야�함.�

• 해결책:�구글의�FlatBuffers를�쓰자.

FlatBuffers

• Facebook�앱에서�FlatBuffers�(Google)을�도입하여�유명해짐.

무�복제�메커니즘

public long id() { int o = __offset(4); return o != 0 ? bb.getLong(o + bb_pos) : 0; }

public String name() { int o = __offset(6); return o != 0 ? __string(o + bb_pos) : null; }

ByteBuffer bb = ByteBuffer.wrap(bytes); MyObject obj = MyObject.getRootAsMyObject(bb); long id = obj.id(); String name = obj.name();

• 사용할�때만�메모리�맵에서�Java�월드로�데이터를�가져올�수�있습니다.

전통적인�수화�(Hydrate)

• 데이터베이스에서�자료를�문자열이나�기타�인코딩된�형태로�가져옵니다.�

• 파싱�혹은�디코딩을�해서�가져온�데이터를�몽땅�Java�객체에�담아줍니다.

CPU와�메모리를�낭비하는�수화

• 데이터베이스에서�자료를�문자열이나�기타�인코딩된�형태로�가져옵니다.�

• 쓰지도�않은�데이터도�다�가져오고�변환까지�거쳤어요.�

• 파싱�혹은�디코딩을�해서�가져온�데이터를�몽땅�Java�객체의�필드�담아줍니다.�

• 쓰지도�않을�데이터를�전부�다시�변환하고�몽땅�필드에�담았어요.�

Realm은�무�복제

현재�row에�대한�정보

컬럼과�로우의�인덱스에�null�혹은�문자열을�저장

실제�데이터가�담긴�테이블

로우와�컬럼을�이용해서�스트링을�가지고�옴

name setName getName

RealmObject

realmGet$name realmSet$name RealmProxyObject

RealmProxyObject

• Annotation�Processing�Tool이�이�작업을�대신해줍니다.

getSupportedOptions getSupportedAnnotationTypes getSupportedSourceVersion

init process

getCompletion

Processor

process 제외 구현 AbstractProcessor

APT�기본�객체

Realm의�APT

어떤�어노테이션�프로세서를�실행할지�결정하는�메타데이터

처리할�어노테이션�지정

• @SupportedAnnoationTypes�어노테이션으로�처리할�어노테이션�지정.

RealmClass�어노테이션을�다�순회하면서…

프록시�인터페이스와�프록시�클래스를�생성

재귀적처리�때문에�최소한�2라운드�진입

process�메서드가�어노테이션�프로세싱의�진입점

APT만으로는�부족합니다.

• APT는�객체를�생성할�수�만�있다.�

• 기존의�객체를�상속받아�확장할�수만�있고�자체를�변경할�수�없음.�

• Realm은�객체의�게터�/�세터를�Realm의�게터�/�세터로�바꾸어야�함.�

• 커스텀�게터와�세트는�동작을�어떻게�확인하고�Realm�게터�/�세터를�적절히�집어넣지?

버전�1.X�시절의�Realm

• 게터와�세터는�표준�이름으로만�생성.�(APT가�예상할�수�없음.)�

• 게터와�세터는�표준�동작만�한다고�가정.�

• 다른�메서드들도�필드에�대해�직접�접근하지�말아야�함.

바이트�코드를�변경하는�것이�해결책

• 바이트�코드를�변경합니다.�

• 필드에�읽으면�그�대신�Realm의�게터를�호출�

• 필드에�무엇을�쓰면�대신�Realm의�세터를�호출�

• 참�쉽죠?

트랜스포머�API

• 안드로이드�그래들�플러그인의�공식적인�후�가공�방법

dependencies에�트랜스포머�등록

android.registerTransform으로트랜스�폼을�빌드�과정에�포함.

transform Transformer

Javassist로 바이트 코드 변경 RealmTransformer

RealmTransformer

왜�이래요?

• 아래의�이점을�위해,�하부�엔진과�실시간�연동합니다.�

• 무�복제�메커니즘�

• 크로스플랫폼�호환성�

• Android,�iOS,�Xamarin,�Javascript에서�호환성을�가집니다.�

• 자동�갱신

One�Realm�to�rule�them�all!

왜�이래요?

• 아래의�이점을�위해,�하부�엔진과�실시간�연동합니다.�

• 무�복제�메커니즘�

• 크로스플랫폼�호환성�

• 자동�갱신�

• 자료는�다른�곳에서�갱신되면�자동으로�갱신이�이루어집니다.

Hello add Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

add

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Blah�blah…

Hello

데이터를�추가하면�리스트�뷰는�자동으로�갱신합니다.데이터는�자동으로�갱신되고�알림이�전달됨�->�알림�때마다�UI를�새로그림.

Realm�객체�생성.

• copyToRealm(object)�-�POJO�인스턴스를�복사�managed�인스턴스�생성.�

• Realm.createObject(class)�-�새로운�managed�인스턴스�생성.Realm.createObject(class,�pk)

• 현재�스키마에서�클래스를�이용해서�테이블을�가져옴.�

• 테이블에서�열을�만들어�가져오고.�

• get을�이용해서�managed�객체를�반환.

��������Table�table�=�schema.getTable(clazz);���������long�rowIndex�=�table.addEmptyRowWithPrimaryKey(primaryKeyValue);���������return�get(clazz,�rowIndex,�acceptDefaultValue,�excludeFields);

Realm.createObject

Schema�(모델과�연관된�RealmObjectSchema와�Table을�관리)

K: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchema

K: RealmModel V: TableK: RealmModel V: TableK: RealmModel V: TableK: RealmModel V: TableK: RealmModel V: Table

K: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: RealmModel V: RealmObjectSchemasK: modelName V: RealmObjectSchema

K: RealmModel V: TableK: RealmModel V: TableK: RealmModel V: TableK: RealmModel V: TableK: modelName V: Table

Table�(실제�스토리지와�연관된�객체)

• addColumn�/�removeColumn�/�renameColumn�…�• addEmptyRow�/�addEmptyRows�/�add�…�• getPrimaryKey�/�hasPrimaryKey�/�isPrimaryKey�…�• getLong(column,�row)�/�getBoolean(column,�row)�…�• findFirstLong(column,�long)�/�findFirstBoolean(column,�boolean)…

Table.java

io_realm_internal_Table.cpp

Table.cpp

Realm�Java

Realm�Core

Java

JNI

Table.java

io_realm_internal_Table.cpp

Table.cpp

베리�빅�빅+�트리

테이블

루트

컬럼으로부터�데이터로이어지는�서브�빅+트리

잠깐만요?�왜�컬럼부터죠?

테이블

루트

컬럼으로부터�데이터로이어지는�서브�빅+트리

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

이름으로�조회했습니다.

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

이메일로�조회했습니다.

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

전통적인�데이타베이스는�한줄�씩�저장합니다.�->�캐쉬�미스

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

�한줄�씩�저장하면�용량이�맞지�않아서�패딩도�넣어야�합니다.

인덱스 이름 이메일 전화번호

1 디카프리오 [email protected] 1004

2 디아즈 [email protected] 1005

3 게이츠 [email protected] 1006

4 갓민우 [email protected] 1007

한줄씩�저장하면�캐쉬히트율이�높고�패딩이�필요없어요.

MVCC�(다중�버전�동시성�제어)

A

B C

D

F G

E

H I

버전�1Root

쓰기�트랜잭션은�새�스냅샷을�만듭니다.

A

B C

D

F G

E

H I

E’

H’

C’

A’

Root 버전�2

쓰기�중에도�읽기가�가능합니다.

A

B C

D

F G

E

H I

E’

H’

C’

A’

버전�2�쓰기중버전�1�읽기�중

읽기는�배타적이지�않습니다.

A

B C

D

F G

E

H I

E’

H’

C’

A’

버전�2�쓰기중유저�2:�버전�1�읽기�중

유저�1:�버전�1�읽기�중

안전합니다.

A

B C

D

F G

E

H I

E’

C’

A’

Root

버전�1

작성�중�프리즈�발생버전�1은�안전

포인터도�여전히�버전�1으로

각기�다른��스레드�로컬을�가집니다.

A

B C

D

F G

E

H I

E’

H’

C’

A’

유저�3:버전�2�쓰기중유저�2:�버전�1�읽기�중

유저�1:�버전�1�읽기�중스레드 로컬 1: 버전 1

스레드 로컬 2: 버전 1

스레드 로컬 3: 버전 1 -> 2

혼란스러울�수�있는�스레드�로컬

• 배타적이지�않게�효율적으로�읽기,�쓰기를�하기�위해메타�데이터를�스레드�단위로�저장.�

• 메모리�맵으로�연결된�데이터는�멀티�스레드로�공유되고�있음.�

• 다만�스레드�로컬로�열린�Realm,�RealmList,�RealmResults,�RealmObjects�객체를�다른�스레드로�전달할�수�없음.�

• 다른�스레드에서�쿼리를�하면�멀티�스레드�자료�공유�덕에�바로�읽음.

정리:�아직�헷갈려요.

• 다른�스레드로�정보를�전달하지�말고�다른�스레드에서�바로�질의하세요.

Gradle�Plugin

• 환경별로�어노테이션�설치는�사용자에게�어렵다.�

• annotationProcessor�-�안드로이드�그래들�플러그인�2.2�이상만�가능.�

• kapt�-�코틀린.�

• apt�-�android-apt를�설치해야�함.�

• 싱크�기능,�어노테이션�전용�코드를�앱�빌드에서�빼기는�사용자에게�어렵다.

대안:�그래들�플러그인어느�객체가�플러그인을�위한�것인지�명기

Plugin<Project>를�상속받아�apply�메서드를�구현

apply�plugin�‘realm-android’

Groovy로�작성되었지만�Java로�짜도�됨.