빠른 시작 - Java SDK
팁
이 가이드에서는 Device Sync를 사용하지 않습니다.
이 가이드 는 장치 로컬 Realm 을 시작하는 데 도움이 될 수 있습니다. 애플리케이션 이 Atlas Device Sync, Realm Functions 또는 사용자 관리 와 같은 기능을 사용하여 네트워크를 통해 백엔드 앱과 통신해야 하는 경우 동기화를 통한 빠른 시작 가이드 를 따라야 합니다.
이 페이지에는 Realm을 앱에 빠르게 통합하기 위한 정보가 포함되어 있습니다. 시작하기 전에 다음 사항이 있는지 확인하세요.
Realm 초기화
앱에서 Realm을 사용하려면 먼저 Realm 라이브러리를 초기화해야 합니다. 애플리케이션은 애플리케이션이 실행될 때마다 Realm을 한 번만 초기화해야 합니다.
Realm 라이브러리를 초기화하려면 Realm.init()
정적 함수에 Android context
를 제공합니다. 동작의 차이 없이 초기화를 위해 활동, 프래그먼트 또는 애플리케이션 context
을(를) 제공할 수 있습니다. 애플리케이션 onCreate()
서브클래스 의 메서드에서 Realm 라이브러리를 초기화할 수 있습니다. 애플리케이션 이 실행될 때마다 Realm 을 한 번만 초기화하도록 합니다.
Realm.init(this); // context, usually an Activity or Application
Realm.init(this) // context, usually an Activity or Application
팁
Android 매니페스트에 애플리케이션 서브클래스 등록
자체 Application
하위 클래스를 생성하는 경우 사용자 지정 애플리케이션 로직을 실행하려면 이를 애플리케이션의 AndroidManifest.xml
에 추가해야 합니다. 매니페스트 애플리케이션 정의의 android.name
속성을 설정하여 사용자가 애플리케이션을 실행할 때 Android가 다른 클래스보다 먼저 Application
하위 클래스를 인스턴스화하도록 합니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mongodb.example"> <application android:name=".MyApplicationSubclass" ... /> </manifest>
객체 모델 정의
애플리케이션의 데이터 모델 은 Realm 내에 저장된 데이터의 구조를 정의합니다. Realm 객체 모델을 사용하여 애플리케이션 코드의 코틀린 (Kotlin) 또는 Java 클래스를 통해 애플리케이션의 데이터 모델 을 정의할 수 있습니다.
애플리케이션의 데이터 모델 을 정의하려면 애플리케이션 코드에 다음 클래스 정의를 추가하세요.
import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; import io.realm.annotations.Required; public class Task extends RealmObject { private String name; private String status = TaskStatus.Open.name(); public void setStatus(TaskStatus status) { this.status = status.name(); } public String getStatus() { return this.status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Task(String _name) { this.name = _name; } public Task() {} }
public enum TaskStatus { Open("Open"), InProgress("In Progress"), Complete("Complete"); String displayName; TaskStatus(String displayName) { this.displayName = displayName; } }
enum class TaskStatus(val displayName: String) { Open("Open"), InProgress("In Progress"), Complete("Complete"), } open class Task() : RealmObject() { var name: String = "task" var status: String = TaskStatus.Open.name var statusEnum: TaskStatus get() { // because status is actually a String and another client could assign an invalid value, // default the status to "Open" if the status is unreadable return try { TaskStatus.valueOf(status) } catch (e: IllegalArgumentException) { TaskStatus.Open } } set(value) { status = value.name } }
Realm 열기
RealmConfiguration
를 사용하여 영역 의 이름이나 위치 , UI 스레드에서 영역 에 대한 동기 읽기 또는 쓰기 허용 여부 등 열려는 영역 의 세부 사항을 제어할 수 있습니다.
String realmName = "My Project"; RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build(); Realm backgroundThreadRealm = Realm.getInstance(config);
val realmName: String = "My Project" val config = RealmConfiguration.Builder().name(realmName).build() val backgroundThreadRealm : Realm = Realm.getInstance(config)
객체 만들기, 읽기, 업데이트 및 삭제
영역 을 연 후에는 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction) ) 차단 에서 해당 영역 내의 객체 를 수정할 수 있습니다.
중요
UI 스레드의 동기식 읽기 및 쓰기
기본적으로 비동기 트랜잭션을 사용하여 애플리케이션의 UI 스레드에서 영역에 읽기 또는 쓰기만 가능합니다. 즉, 동기 메서드의 사용을 명시적으로 허용하지 않는 한 Android 애플리케이션의 기본 스레드에서 이름이 Async
(이)라는 단어로 끝나는 Realm
메서드만 사용할 수 있습니다.
이러한 제한은 애플리케이션 사용자를 위해 존재합니다. 즉, UI 스레드에서 읽기 및 쓰기 작업을 수행하면 UI 상호 작용이 응답하지 않거나 느려질 수 있으므로 일반적으로 이러한 작업을 비동기적으로 처리하거나 배경 스레드에서 처리하는 것이 가장 좋습니다. 하지만 애플리케이션이 UI 스레드에서 동기 영역 읽기 또는 쓰기를 사용해야 하는 경우 다음 SyncConfiguration
옵션을 사용하여 동기 메서드 사용을 명시적으로 허용할 수 있습니다.
SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build(); Realm.getInstanceAsync(config, new Realm.Callback() { public void onSuccess(Realm realm) { Log.v( "EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread." ); } });
val config = SyncConfiguration.Builder(app.currentUser(), PARTITION) .allowQueriesOnUiThread(true) .allowWritesOnUiThread(true) .build() Realm.getInstanceAsync(config, object : Realm.Callback() { override fun onSuccess(realm: Realm) { Log.v("EXAMPLE", "Successfully opened a realm with reads and writes allowed on the UI thread.") } })
새 Task
을 만들려면 Task
클래스의 인스턴스를 인스턴스화하고 쓰기 블록의 Realm에 추가합니다.
Task Task = new Task("New Task"); backgroundThreadRealm.executeTransaction (transactionRealm -> { transactionRealm.insert(Task); });
val task : Task = Task() task.name = "New Task" backgroundThreadRealm.executeTransaction { transactionRealm -> transactionRealm.insert(task) }
영역 에 있는 모든 항목의 라이브 컬렉션 을 조회 할 수 있습니다.
// all Tasks in the realm RealmResults<Task> Tasks = backgroundThreadRealm.where(Task.class).findAll();
// all tasks in the realm val tasks : RealmResults<Task> = backgroundThreadRealm.where<Task>().findAll()
필터를 사용하여 해당 컬렉션 을 필터하다 할 수도 있습니다 .
// you can also filter a collection RealmResults<Task> TasksThatBeginWithN = Tasks.where().beginsWith("name", "N").findAll(); RealmResults<Task> openTasks = Tasks.where().equalTo("status", TaskStatus.Open.name()).findAll();
// you can also filter a collection val tasksThatBeginWithN : List<Task> = tasks.where().beginsWith("name", "N").findAll() val openTasks : List<Task> = tasks.where().equalTo("status", TaskStatus.Open.name).findAll()
작업을 수정하려면 쓰기 트랜잭션(write transaction) 차단에서 해당 속성을 업데이트합니다.
Task otherTask = Tasks.get(0); // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction( transactionRealm -> { Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.getName()).findFirst(); innerOtherTask.setStatus(TaskStatus.Complete); });
val otherTask: Task = tasks[0]!! // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction { transactionRealm -> val innerOtherTask : Task = transactionRealm.where<Task>().equalTo("name", otherTask.name).findFirst()!! innerOtherTask.status = TaskStatus.Complete.name }
마지막으로 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction) ) 차단 에서 deleteFromRealm()
메서드를 호출하여 작업 을 삭제 수 있습니다.
Task yetAnotherTask = Tasks.get(0); String yetAnotherTaskName = yetAnotherTask.getName(); // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction( transactionRealm -> { Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskName).findFirst(); innerYetAnotherTask.deleteFromRealm(); });
val yetAnotherTask: Task = tasks.get(0)!! val yetAnotherTaskName: String = yetAnotherTask.name // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction { transactionRealm -> val innerYetAnotherTask : Task = transactionRealm.where<Task>().equalTo("name", yetAnotherTaskName).findFirst()!! innerYetAnotherTask.deleteFromRealm() }
변화를 주시하세요
addChangeListener()
메서드와 함께 사용자 지정 OrderedRealmCollectionChangeListener
를 첨부하여 영역, 컬렉션 또는 객체 의 변경 사항 을 확인할 수 있습니다.
// all Tasks in the realm RealmResults<Task> Tasks = uiThreadRealm.where(Task.class).findAllAsync(); Tasks.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Task>>() { public void onChange(RealmResults<Task> collection, OrderedCollectionChangeSet changeSet) { // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (OrderedCollectionChangeSet.Range range : deletions) { Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) { Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) { Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } } });
// all tasks in the realm val tasks : RealmResults<Task> = realm.where<Task>().findAllAsync() tasks.addChangeListener(OrderedRealmCollectionChangeListener<RealmResults<Task>> { collection, changeSet -> // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate val deletions = changeSet.deletionRanges for (i in deletions.indices.reversed()) { val range = deletions[i] Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } val insertions = changeSet.insertionRanges for (range in insertions) { Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } val modifications = changeSet.changeRanges for (range in modifications) { Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } })
전체 예시
새 Android Studio 프로젝트에서 이 프로젝트를 실행하는 경우 이 파일을 복사하여 애플리케이션의 MainActivity
에 붙여넣을 수 있습니다. 단, 다음 사항에 유의하세요.
자신의 프로젝트에 대해 파일 상단에 패키지 선언을 사용합니다.
Java를 사용하는 경우
Task
및TaskStatus
에 대한import
문을 업데이트 합니다.
import io.realm.RealmObject; import io.realm.annotations.PrimaryKey; import io.realm.annotations.Required; public class Task extends RealmObject { private String name; private String status = TaskStatus.Open.name(); public void setStatus(TaskStatus status) { this.status = status.name(); } public String getStatus() { return this.status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Task(String _name) { this.name = _name; } public Task() {} }
public enum TaskStatus { Open("Open"), InProgress("In Progress"), Complete("Complete"); String displayName; TaskStatus(String displayName) { this.displayName = displayName; } }
import io.realm.OrderedCollectionChangeSet; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import io.realm.OrderedRealmCollectionChangeListener; import io.realm.Realm; import io.realm.RealmConfiguration; import io.realm.RealmResults; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import com.mongodb.realm.examples.model.java.Task; import com.mongodb.realm.examples.model.java.TaskStatus; public class MainActivity extends AppCompatActivity { Realm uiThreadRealm; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Realm.init(this); // context, usually an Activity or Application String realmName = "My Project"; RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build(); uiThreadRealm = Realm.getInstance(config); addChangeListenerToRealm(uiThreadRealm); FutureTask<String> Task = new FutureTask(new BackgroundQuickStart(), "test"); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(Task); } private void addChangeListenerToRealm(Realm realm) { // all Tasks in the realm RealmResults<Task> Tasks = uiThreadRealm.where(Task.class).findAllAsync(); Tasks.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Task>>() { public void onChange(RealmResults<Task> collection, OrderedCollectionChangeSet changeSet) { // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (OrderedCollectionChangeSet.Range range : deletions) { Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) { Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) { Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length - 1)); } } }); } protected void onDestroy() { super.onDestroy(); // the ui thread realm uses asynchronous transactions, so we can only safely close the realm // when the activity ends and we can safely assume that those transactions have completed uiThreadRealm.close(); } public class BackgroundQuickStart implements Runnable { public void run() { String realmName = "My Project"; RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build(); Realm backgroundThreadRealm = Realm.getInstance(config); Task Task = new Task("New Task"); backgroundThreadRealm.executeTransaction (transactionRealm -> { transactionRealm.insert(Task); }); // all Tasks in the realm RealmResults<Task> Tasks = backgroundThreadRealm.where(Task.class).findAll(); // you can also filter a collection RealmResults<Task> TasksThatBeginWithN = Tasks.where().beginsWith("name", "N").findAll(); RealmResults<Task> openTasks = Tasks.where().equalTo("status", TaskStatus.Open.name()).findAll(); Task otherTask = Tasks.get(0); // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction( transactionRealm -> { Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.getName()).findFirst(); innerOtherTask.setStatus(TaskStatus.Complete); }); Task yetAnotherTask = Tasks.get(0); String yetAnotherTaskName = yetAnotherTask.getName(); // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction( transactionRealm -> { Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskName).findFirst(); innerYetAnotherTask.deleteFromRealm(); }); // because this background thread uses synchronous realm transactions, at this point all // transactions have completed and we can safely close the realm backgroundThreadRealm.close(); } } }
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import android.util.Log import io.realm.* import io.realm.annotations.PrimaryKey import io.realm.annotations.Required import io.realm.kotlin.where import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.FutureTask class MainActivity : AppCompatActivity() { lateinit var uiThreadRealm: Realm override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Realm.init(this) // context, usually an Activity or Application val realmName: String = "My Project" val config = RealmConfiguration.Builder() .name(realmName) .build() uiThreadRealm = Realm.getInstance(config) addChangeListenerToRealm(uiThreadRealm) val task : FutureTask<String> = FutureTask(BackgroundQuickStart(), "test") val executorService: ExecutorService = Executors.newFixedThreadPool(2) executorService.execute(task) } fun addChangeListenerToRealm(realm : Realm) { // all tasks in the realm val tasks : RealmResults<Task> = realm.where<Task>().findAllAsync() tasks.addChangeListener(OrderedRealmCollectionChangeListener<RealmResults<Task>> { collection, changeSet -> // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate val deletions = changeSet.deletionRanges for (i in deletions.indices.reversed()) { val range = deletions[i] Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } val insertions = changeSet.insertionRanges for (range in insertions) { Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } val modifications = changeSet.changeRanges for (range in modifications) { Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length - 1}") } }) } override fun onDestroy() { super.onDestroy() // the ui thread realm uses asynchronous transactions, so we can only safely close the realm // when the activity ends and we can safely assume that those transactions have completed uiThreadRealm.close() } class BackgroundQuickStart : Runnable { override fun run() { val realmName: String = "My Project" val config = RealmConfiguration.Builder().name(realmName).build() val backgroundThreadRealm : Realm = Realm.getInstance(config) val task : Task = Task() task.name = "New Task" backgroundThreadRealm.executeTransaction { transactionRealm -> transactionRealm.insert(task) } // all tasks in the realm val tasks : RealmResults<Task> = backgroundThreadRealm.where<Task>().findAll() // you can also filter a collection val tasksThatBeginWithN : List<Task> = tasks.where().beginsWith("name", "N").findAll() val openTasks : List<Task> = tasks.where().equalTo("status", TaskStatus.Open.name).findAll() val otherTask: Task = tasks[0]!! // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction { transactionRealm -> val innerOtherTask : Task = transactionRealm.where<Task>().equalTo("name", otherTask.name).findFirst()!! innerOtherTask.status = TaskStatus.Complete.name } val yetAnotherTask: Task = tasks.get(0)!! val yetAnotherTaskName: String = yetAnotherTask.name // all modifications to a realm must happen inside of a write block backgroundThreadRealm.executeTransaction { transactionRealm -> val innerYetAnotherTask : Task = transactionRealm.where<Task>().equalTo("name", yetAnotherTaskName).findFirst()!! innerYetAnotherTask.deleteFromRealm() } // because this background thread uses synchronous realm transactions, at this point all // transactions have completed and we can safely close the realm backgroundThreadRealm.close() } } } enum class TaskStatus(val displayName: String) { Open("Open"), InProgress("In Progress"), Complete("Complete"), } open class Task() : RealmObject() { var name: String = "task" var status: String = TaskStatus.Open.name var statusEnum: TaskStatus get() { // because status is actually a String and another client could assign an invalid value, // default the status to "Open" if the status is unreadable return try { TaskStatus.valueOf(status) } catch (e: IllegalArgumentException) { TaskStatus.Open } } set(value) { status = value.name } }
출력
위의 코드를 실행하면 다음과 유사한 출력이 생성됩니다.
Successfully authenticated anonymously. Updated range: 0 to 1 Deleted range: 0 to 1 Successfully logged out.