チュートリアル: Atlas Device Sync for Kotlin
項目一覧
完了までの推定時間: 30分( Kotlin の経験により異なります)
Realm は Kotlin SDK を提供しており、 J件名 を使用して Kotlin で Android モバイル アプリケーションを作成できます。 。このチュートリアルでは、ToDo アイテム リスト管理アプリケーションの作成を説明する、 kotlin.todo.flex
という名前の Kotlin Flexible Sync テンプレート アプリに基づいています。 このアプリケーションを使用すると、ユーザーは次のことが可能になります。
メールを新しいユーザー アカウントとして登録します。
メールとパスワードを使用してアカウントにサインインします(その後サインアウトします)。
自分のタスクを表示、作成、変更、削除します。
ユーザーが所有者でない場合でも、すべてのタスクを表示します。
また、テンプレートアプリには、デバイスがオフラインモードになっていることをシミュレートするトグルが用意されています。 このトグルを使用すると、シミュレーターで Device Sync 機能をすばやくテストして、インターネットに接続していないユーザーをエミュレートできます。 ただし、本番アプリケーションではこのトグルを削除する可能性が高いでしょう。
このチュートリアルでは、テンプレート アプリに機能を追加します。 既存のItem
モデルに新しいPriority
フィールドを追加し、 Flexible Sync サブスクライブを更新して、優先順位の範囲内のアイテムのみを表示します。 この例では、テンプレート アプリを独自のニーズに合わせて調整する方法について説明しています。
学習目的
このチュートリアルでは、テンプレート アプリを独自のニーズに合わせて調整する方法について説明します。
このチュートリアルでは、次の方法を学習します。
Realm オブジェクトモデルを重大じゃない変更で更新します。
Device Sync サブスクライブを更新します。
同期されるデータを変更するには、サーバー上の Device Sync 構成にクエリ可能なフィールドを追加します。
注意
クイック スタートを確認する
ガイド付きチュートリアルに従うのではなく、独自のアプリケーションを使い始める場合は、 Kotlin クイック スタートを確認できます。 コピー可能なコード サンプルと、Atlas App Services バックエンドを設定するために必要な重要な情報が含まれています。
前提条件
Android Studio バンドル式2021 。1 。1またはそれ以上
JDK 11 以上
Kotlin Plugin for Android Studio バージョン 1.6.10 以上
サポート対象の CPU アーキテクチャが使用されている Android Virtual Device(AVD)
このチュートリアルは、テンプレート アプリから始めます。 テンプレート アプリを作成するには、 Atlas アカウント、 API キー、 App Services CLI が必要です。
Atlas アカウントの作成の詳細については、「 Atlas の使用開始」ドキュメントを参照してください。 このチュートリアルでは、無料階層クラスターを持つ Atlas アカウントが必要です。
ログインする MongoDB Cloud アカウントの Atlas API キーも必要です。 App Services CLI を使用してテンプレート アプリを作成するには、プロジェクト オーナーである必要があります。
App Services CLI のインストールについて詳しくは、「 App Services CLI のインストール」を参照してください。 インストール後、Atlas プロジェクトの API キーを使用して「 login 」コマンドを実行します。
テンプレートを使用して開始
このチュートリアルは、 kotlin.todo.flex
という名前の Kotlin SDK Flexible Sync テンプレート アプリを基本にして作成されており、 デフォルトのアプリから始めて、新しい機能について説明しています。
テンプレート アプリの詳細については、「テンプレート アプリ 」を参照してください。
Atlas アカウントがまだない場合は、テンプレート アプリを配置するためにサインアップしてください。
「アプリの作成」ガイドに記載されている手順に従い、 Create App from Templateを選択します。 Real-time Syncテンプレートを選択します。 これにより、Device Sync テンプレート App Services App クライアントの 1 つで使用するために事前構成された App Services App が作成されます。
テンプレート アプリを作成すると、UI に Get the Front-end Code for your Template というラベルの付いたモーダルが表示されます。このモーダルには、テンプレート アプリのクライアントコードを .zip
ファイルとしてダウンロードする方法や、App Services CLI を使用してクライアントを取得する方法が表示されます。
.zip
または App Services CLI メソッドを選択した後、画面の指示に従ってクライアント コードを取得します。このチュートリアルでは、Kotlin (Android) クライアント コードを選択します。
注意
デフォルトの Windows ZIP ユーティリティで、.zip ファイルが空白ファイルとして表示される。この問題が発生した場合は、利用可能なサードパーティの zip プログラムのいずれかを使用してください。
appservices apps createコマンドでバックエンドを設定し、 Kotlin テンプレート アプリを作成してこのチュートリアルのベースとして使用します。
ターミナル ウィンドウで次のコマンドを実行して、「Myチュートリアル App」という名前のアプリを作成し、環境が「開発」(本番環境やQAではなく)に設定されているUS-VA
リージョンに配置されます。
appservices app create \ --name MyTutorialApp \ --template kotlin.todo.flex \ --deployment-model global \ --environment development
このコマンドは、 --name
フラグの値と同じ名前で現在のパスに新しいディレクトリを作成します。
Device Sync クライアントコードを含む Github リポジトリをフォークしてクローンできます。 Kotlin クライアント コードは https://github.com/mongodb/template-app-kotlin-todo で入手できます。
このプロセスを使用してクライアントのコードを取得する場合は、クライアントで使用するテンプレート アプリを作成する必要があります。 「テンプレート アプリの作成」 の手順に従って、Atlas App Services UI、App Services CLI、または管理 API を使用して Device Sync テンプレート アプリを作成します。
テンプレート アプリの設定
アプリ構造の探索
Android Studio がプロジェクトをインデックス化している間に、プロジェクト組織を調べます。 app/java/com.mongodb.app
ディレクトリ内には、注目に値するファイルがいくつかあります。
ファイル | 目的 |
---|---|
ComposeItemActivity.kt | 配置を定義し、Realm のオープン、Realm へのアイテムの書込み、ユーザーのログアウト、Realm を閉じるための機能を提供する アクティビティ クラス。 |
ComposLoginActivity.kt | レイアウトを定義し、ユーザーを登録してユーザーをログインするための機能を提供するアクティビティ クラス。 |
TemplateApp.kt | App Services Appを初期化するクラス。 |
このチュートリアルでは、次のファイルを操作します。
ファイル | 目的 |
---|---|
Item.kt | domain ディレクトリにあります。 データベースに保存する Realm オブジェクトを定義します。 |
AddItem.kt | ui/tasks ディレクトリにあります。 アイテムを追加するときに使用される配置を定義する 複合 関数が含まれています。 |
AddItemViewModel.kt | presentation/tasks ディレクトリにあります。 ビジネス ロジックを含み、アイテムを追加するときに状態を管理するビュー モデル。 |
SyncRepository.kt | data ディレクトリにあります。 Realm Sync にアクセスするために使用され、Flexible Sync サブスクリプションを定義するリポジトリ。 |
String.xl | res/values ディレクトリにあります。 テキスト string リソース を定義します アプリで使用されます。 |
バックエンドの確認
Atlas App Servicesにログインします。 Data Servicesタブで、 Browse Collectionsをクリックします。 データベースのリストで、 todoデータベース、 Itemコレクションを検索して展開します。 このコレクションで作成したドキュメントが表示されます。
アプリケーションを変更する
新しいプロパティの追加
モデルへの新しいプロパティの追加
すべて期待どおりに動作していることが確認できたため、変更を追加できます。 このチュートリアルでは、各アイテムに「優先順位」プロパティを追加し、アイテムに優先順位をつけてフィルタリングできるようにしました。 優先順位プロパティをPriorityLevel
列挙型にマッピングして、可能な値を制限します。各列挙型の序数を使用して優先順位整数に対応するようにします。これにより、後で数値優先レベルに基づいてクエリを実行できるようになります。
これを行うには、次の手順に従います。
app/java/com.mongodb.app/domain
フォルダ内で、Item
クラスファイルを開きます。可能な値を制限するには、
PriorityLevel
列挙を追加します。 また、Item
クラスにpriority
プロパティを追加します。これにより、デフォルトの優先順位が 3 に設定されます。これは、優先順位の低い Todo アイテムであることを示します。domain/Item.kt// ... imports enum class PriorityLevel() { Severe, // priority 0 High, // priority 1 Medium, // priority 2 Low // priority 3 } class Item() : RealmObject { var _id: ObjectId = ObjectId.create() var isComplete: Boolean = false var summary: String = "" var owner_id: String = "" var priority: Int = PriorityLevel.Low.ordinal constructor(ownerId: String = "") : this() { owner_id = ownerId } // ... equals() and hashCode() functions }
新しいアイテムの作成時の優先順位の設定
ui/tasks
フォルダから、AddItem.kt
ファイルを開きます。 このファイルは、ユーザーが [+] ボタンをクリックして新しい Todo 項目を追加するときに表示される UI の複合関数を定義します。まず、
package com.mongodb.app
の下に次のインポートを追加します。ui/tasks/AddItem.ktimport androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.mongodb.app.domain.PriorityLevel ここで、ユーザーが優先順位列挙型を使用可能な値として使用してリストから優先度レベルを選択できるようにする
AddItemPrompt
にドロップダウン フィールドを追加します。ui/tasks/AddItem.kt// ... imports fun AddItemPrompt(viewModel: AddItemViewModel) { AlertDialog( containerColor = Color.White, onDismissRequest = { viewModel.closeAddTaskDialog() }, title = { Text(stringResource(R.string.add_item)) }, text = { Column { Text(stringResource(R.string.enter_item_name)) TextField( colors = ExposedDropdownMenuDefaults.textFieldColors(containerColor = Color.White), value = viewModel.taskSummary.value, maxLines = 2, onValueChange = { viewModel.updateTaskSummary(it) }, label = { Text(stringResource(R.string.item_summary)) } ) val priorities = PriorityLevel.values() ExposedDropdownMenuBox( modifier = Modifier.padding(16.dp), expanded = viewModel.expanded.value, onExpandedChange = { viewModel.open() }, ) { TextField( readOnly = true, value = viewModel.taskPriority.value.name, onValueChange = {}, label = { Text(stringResource(R.string.item_priority)) }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = viewModel.expanded.value) }, colors = ExposedDropdownMenuDefaults.textFieldColors(), modifier = Modifier .fillMaxWidth() .menuAnchor() ) ExposedDropdownMenu( expanded = viewModel.expanded.value, onDismissRequest = { viewModel.close() } ) { priorities.forEach { DropdownMenuItem( text = { Text(it.name) }, onClick = { viewModel.updateTaskPriority(it) viewModel.close() } ) } } } } }, // ... buttons ) } Android Studio はいくつかのエラーを識別します。 次のステップでは、関連する関数を追加して、これらを修正します。
次に、ドロップダウン フィールド ラベルを string リソースとして定義します。
res/values/strings.xml
ファイルを開き、「resource」要素を閉じる前に以下を追加します。res/values/strings.xl<string name="item_priority">Item Priority</string> presentation/tasks
フォルダー内で、AddItemViewModel.kt
ファイルを開きます。 ここで、新しいドロップダウン フィールドに関連するビジネス ロジックを追加します。package com.mongodb.app
の下にPriorityLevel
インポートを追加し、ドロップダウン内の状態変更を処理するために必要なAddItemViewModel
クラスに変数と関数を追加します。presentation/tasks/AddItemViewModel.kt// ... imports import com.mongodb.app.domain.PriorityLevel // ... events class AddItemViewModel( private val repository: SyncRepository ) : ViewModel() { private val _addItemPopupVisible: MutableState<Boolean> = mutableStateOf(false) val addItemPopupVisible: State<Boolean> get() = _addItemPopupVisible private val _taskSummary: MutableState<String> = mutableStateOf("") val taskSummary: State<String> get() = _taskSummary private val _taskPriority: MutableState<PriorityLevel> = mutableStateOf(PriorityLevel.Low) val taskPriority: State<PriorityLevel> get() = _taskPriority private val _expanded: MutableState<Boolean> = mutableStateOf(false) val expanded: State<Boolean> get() = _expanded private val _addItemEvent: MutableSharedFlow<AddItemEvent> = MutableSharedFlow() val addItemEvent: Flow<AddItemEvent> get() = _addItemEvent fun openAddTaskDialog() { _addItemPopupVisible.value = true } fun closeAddTaskDialog() { cleanUpAndClose() } fun updateTaskSummary(taskSummary: String) { _taskSummary.value = taskSummary } fun updateTaskPriority(taskPriority: PriorityLevel) { _taskPriority.value = taskPriority } fun open() { _expanded.value = true } fun close() { _expanded.value = false } // addTask() and cleanUpAndClose() functions } ここで、
addTask()
関数とcleanUpAndClose()
関数を更新して新しいtaskPriority
パラメーターを含め、優先順位情報でメッセージを更新し、アイテム追加ビューが閉じられたら優先順位フィールドを低にリセットします。fun addTask() { CoroutineScope(Dispatchers.IO).launch { runCatching { repository.addTask(taskSummary.value, taskPriority.value) }.onSuccess { withContext(Dispatchers.Main) { _addItemEvent.emit(AddItemEvent.Info("Task '$taskSummary' with priority '$taskPriority' added successfully.")) } }.onFailure { withContext(Dispatchers.Main) { _addItemEvent.emit(AddItemEvent.Error("There was an error while adding the task '$taskSummary'", it)) } } cleanUpAndClose() } } private fun cleanUpAndClose() { _taskSummary.value = "" _taskPriority.value = PriorityLevel.Low _addItemPopupVisible.value = false } 最後に、
data
フォルダーからSyncRepository.kt
ファイルを開き、アイテムを Realm に書込むaddTask()
関数で同じ変更を反映します。まず、
package com.mongodb.app
の下にPriorityLevel
インポートを追加し、addTask()
関数を更新してtaskPriority
をパラメーターとして渡し、priority
フィールドを整数として Realm に書込みます(列挙序数を使用)。data/SyncRepository.kt// ... imports import com.mongodb.app.domain.PriorityLevel interface SyncRepository { // ... Sync functions suspend fun addTask(taskSummary: String, taskPriority: PriorityLevel) // ... Sync functions } class RealmSyncRepository( onSyncError: (session: SyncSession, error: SyncException) -> Unit ) : SyncRepository { // ... variables and SyncConfiguration initializer // ... Sync functions override suspend fun addTask(taskSummary: String, taskPriority: PriorityLevel) { val task = Item().apply { owner_id = currentUser.id summary = taskSummary priority = taskPriority.ordinal } realm.write { copyToRealm(task) } } override suspend fun updateSubscriptions(subscriptionType: SubscriptionType) { realm.subscriptions.update { removeAll() val query = when (subscriptionType) { SubscriptionType.MINE -> getQuery(realm, SubscriptionType.MINE) SubscriptionType.ALL -> getQuery(realm, SubscriptionType.ALL) } add(query, subscriptionType.name) } } // ... additional Sync functions } class MockRepository : SyncRepository { override fun getTaskList(): Flow<ResultsChange<Item>> = flowOf() override suspend fun toggleIsComplete(task: Item) = Unit override suspend fun addTask(taskSummary: String, taskPriority: PriorityLevel) = Unit override suspend fun updateSubscriptions(subscriptionType: SubscriptionType) = Unit override suspend fun deleteTask(task: Item) = Unit override fun getActiveSubscriptionType(realm: Realm?): SubscriptionType = SubscriptionType.ALL override fun pauseSync() = Unit override fun resumeSync() = Unit override fun isTaskMine(task: Item): Boolean = task.owner_id == MOCK_OWNER_ID_MINE override fun close() = Unit // ... companion object }
およびテストの実行
この時点で、アプリケーションを再実行できます。 このチュートリアルの前半で作成したアカウントを使用してログインします。 過去に作成した 1 つのアイテムが表示されます。 新しいアイテムを追加すると、優先順位を設定できるようになります。 優先順位をHigh
に選択し、アイテムを保存します。
ブラウザで Atlas データページに切り替え、 Item
コレクションを更新します。 これでpriority
フィールドが追加され、 1に設定された新しいアイテムが表示されます。 既存のアイテムにはpriority
フィールドはありません。
注意
同期が中断されなかった理由
Realm オブジェクトにプロパティを追加しても重大な変更ではないため、クライアントをリセットする必要はありません。 テンプレート アプリでは開発モードが有効になっているため、クライアント Realm オブジェクトへの変更はサーバー側のスキーマに反映されます。 詳しくは、「開発モードとデータモデルの更新 」を参照してください。
サブスクリプションを変更する
サブスクライブを更新する
app/java/com.mongodb.app/data
フォルダー内で、 SyncRepository.kt
ファイルを開き、Flexible Sync サブスクリプションを定義します。 サブスクリプションは、ユーザーのデバイスおよびアカウントと同期するドキュメントを定義します。 getQuery()
関数を見つけます。 現在 2 つのサブスクライブをサブスクライブしていることがわかります。
MINE
:ownerId
プロパティが認証されたユーザーと一致するすべてのドキュメント。ALL
: すべてのユーザーからのすべてのドキュメント。
MINE
サブスクライブを更新して、優先度が「高」または「重要」とマークされたアイテムのみを同期するようにしたいと考えています。
ご存知のとおり、 priority
フィールドのタイプはint
で、最高優先順位("Severe")の値は 0、最低優先順位("Low")の値は 3 です。次が可能です:整数と優先順位プロパティを直接比較します。 そのためには、次のように、優先順位が PerformanceLevel.High(または 1)より小さいドキュメントを含むように RQL ステートメントを編集します。
private fun getQuery(realm: Realm, subscriptionType: SubscriptionType): RealmQuery<Item> = when (subscriptionType) { SubscriptionType.MINE -> realm.query("owner_id == $0 AND priority <= ${PriorityLevel.High.ordinal}", currentUser.id) SubscriptionType.ALL -> realm.query() }
また、アプリを開くたびに同期するドキュメントを再計算するようにサブスクライブ クエリを強制します。
そのためには、アプリケーションの起動時に呼び出されるSyncConfiguration.Builder().initialSubscriptions()
関数を見つけます。 まず、 reRunOnOpen
パラメータ設定をtrue
に追加し、次にupdateExisting
をtrue
に設定します。これにより、既存のクエリを更新できるようになります。
config = SyncConfiguration.Builder(currentUser, setOf(Item::class)) .initialSubscriptions(rerunOnOpen = true) { realm -> // Subscribe to the active subscriptionType - first time defaults to MINE val activeSubscriptionType = getActiveSubscriptionType(realm) add(getQuery(realm, activeSubscriptionType), activeSubscriptionType.name, updateExisting = true) } .errorHandler { session: SyncSession, error: SyncException -> onSyncError.invoke(session, error) } .waitForInitialRemoteData() .build()
およびテストの実行
アプリケーションを再度実行します。このチュートリアルの前半で作成したアカウントを使用してログインします。
Realm がドキュメントコレクションを再同期する最初の瞬間、作成した優先順位の高い新しいアイテムが表示されます。
Tip
開発者モードが有効になっている場合にサブスクリプションを変更する
このチュートリアルでは、サブスクリプションを変更し、優先順位フィールドを初めてクエリすると、フィールドは Device Sync Collection Queryable Fieldsに自動的に追加されます。 これは、テンプレート アプリで開発モードがデフォルトで有効になっているために発生します。 開発モードが有効になっていない場合、クライアント側の同期クエリで使用するには、フィールドをクエリ可能なフィールドとして手動で追加する必要があります。
詳細については、「クエリ可能なフィールド 」を参照してください。
機能をさらにテストしたい場合は、さまざまな優先順位のアイテムを作成できます。 優先順位が「高」より低いアイテムを追加しようとすると、権限がないことを示す Toast メッセージが表示されます。 また、 Logcat を使用してログを 確認すると、 、アイテムが「正常に追加」されたことを示すメッセージと、その後に同期エラーが表示されます。
ERROR "Client attempted a write that is outside of permissions or query filters; it has been reverted"
これは、このシナリオでは Realm はアイテムをローカルに作成し、バックエンドと同期した後、サブスクライブ ルールを満たしていないため書込みを元に戻すためです。
また、最初に作成したドキュメントは優先順位がnull
になっているため、同期されないことにも注意してください。 このアイテムを同期する場合は、Atlas UI でドキュメントを編集し、優先順位フィールドに値を追加します。
まとめ
既存の Realm オブジェクトにプロパティを追加することは重大じゃない変更であり、 開発モード によってスキーマの変更がサーバー側に反映されるようになります。
次のステップ
Kotlin SDKドキュメントをお読みください。
MongoDB Communityに参加して、他の MongoDB 開発者や技術専門家から学びます。
エンジニアリング プロジェクトと専門家が提供するサンプルプロジェクトを探索します。
注意
フィードバックの共有
ではどのようにGoしましたか。 ページ右下にあるRate this pageウィジェットを使用して、有効性を評価します。 またはGithub リポジトリ に問題を報告する 問題が発生した場合は、。