注意
捆绑同步 Realm
SDK 版本 10.9.0 引入了捆绑同步 Realm 的能力。在 10.9.0 版本之前,您只能捆绑本地 Realm。
Realm 支持捆绑Realm 文件。 捆绑 Realm 文件时,您将在应用程序下载中包含一个数据库及其所有数据。
这允许用户使用一设立初始数据首次启动应用程序。 对于同步 Realm,捆绑可以避免用户首次打开应用程序时长时间的初始下载。 相反,用户必须只下载自生成捆绑文件以来发生的同步更改。
重要
捆绑同步 Realm
如果您的后端应用程序使用 Flexible Sync ,则用户在首次打开捆绑域文件时可能会遇到客户端重置的情况。 启用客户端最大离线时间(默认启用客户端最大离线客户端)时,可能会出现这种情况。 如果在用户首次同步之前生成捆绑域文件的时间超过了客户端最大离线时间设置指定的天数,则用户会遇到客户端重置。
执行客户端重置的应用程序会从应用程序后端下载 Realm 的完整状态。 这抵消了捆绑 Realm 文件的优势。 为防止客户端重置并保留 Realm 文件捆绑的优点,请执行以下操作:
避免在捆绑同步 Realm 的应用程序中使用客户端最大离线时间。
如果您的应用程序确实使用了客户端最大离线时间,请确保您的应用程序下载始终包含最近同步的 Realm 文件。 为每个应用程序版本生成一个新文件,并确保任何版本保持最新状态的时间都不会超过客户端最大离线时间天数。
Overview
要创建 Realm 文件并将其与应用程序捆绑在一起,请执行以下操作:
创建一个 Realm 文件,其中包含要捆绑的数据。
将 Realm 文件捆绑到生产应用程序的
/<app name>/src/main/assets文件夹中。在生产应用程序中,从捆绑的资产文件中打开 Realm 。 对于同步 Realm,您必须提供分区键。
注意
仅限同类型同步
此方法仅支持复制另一个基于分区的同步用户的基于分区的同步配置,或复制另一个 Flexible Sync 用户的 Flexible Sync 配置。 您不能使用此方法在基于分区的同步 Realm 和 Flexible Sync Realm 之间进行转换,反之亦然。
创建用于捆绑的 Realm 文件
构建一个共享应用程序数据模型的临时 Realm 应用。
打开一个 Realm 并添加要捆绑的数据。 如果使用同步 Realm,请留出时间让 Realm 完全同步。
使用writeCopyTo()方法将 Realm 复制到新文件中:
String appID = YOUR_APP_ID; // replace this with your App ID App app = new App(appID); Credentials anonymousCredentials = Credentials.anonymous(); app.loginAsync(anonymousCredentials, it -> { if (it.isSuccess()) { Log.v("EXAMPLE", "Successfully authenticated anonymously."); String PARTITION = "PARTITION_YOU_WANT_TO_BUNDLE"; // you can only create realm copies on a background thread with a looper. // HandlerThread provides a Looper-equipped thread. HandlerThread handlerThread = new HandlerThread("CopyARealmHandler"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()); handler.post(new Thread(new Runnable() { public void run() { SyncConfiguration config = new SyncConfiguration.Builder(app.currentUser(), PARTITION) // wait for the realm to download all data from the backend before opening .waitForInitialRemoteData() .build(); Realm realm = Realm.getInstance(config); Log.v("EXAMPLE", "Successfully opened a realm."); // write a copy of the realm you can manually copy to your production application assets File outputDir = activity.getApplicationContext().getCacheDir(); File outputFile = new File(outputDir.getPath() + "/" + PARTITION + "_bundled.realm"); // ensure all local changes have synced to the backend try { app.getSync().getSession(config).uploadAllLocalChanges(10000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } // cannot write to file if it already exists. Delete the file if already there outputFile.delete(); realm.writeCopyTo(outputFile); // search for this log line to find the location of the realm copy Log.i("EXAMPLE", "Wrote copy of realm to " + outputFile.getAbsolutePath()); // always close a realm when you're done using it realm.close(); }})); } else { Log.e("EXAMPLE", "Failed to authenticate: " + it.getError().toString()); } }); val appID: String = YOUR_APP_ID // replace this with your App ID val app = App(appID) val anonymousCredentials = Credentials.anonymous() app.loginAsync(anonymousCredentials) { it: App.Result<User?> -> if (it.isSuccess) { Log.v("EXAMPLE", "Successfully authenticated anonymously.") val PARTITION = "PARTITION_YOU_WANT_TO_BUNDLE" // you can only create realm copies on a background thread with a looper. // HandlerThread provides a Looper-equipped thread. val handlerThread = HandlerThread("CopyARealmHandler") handlerThread.start() val handler = Handler(handlerThread.looper) handler.post(Thread { val config = SyncConfiguration.Builder(app.currentUser(), PARTITION) // wait for the realm to download all data from the backend before opening .waitForInitialRemoteData() .build() val realm : Realm = Realm.getInstance(config); Log.v("EXAMPLE", "Successfully opened a realm.") // write a copy of the realm you can manually copy to your production application assets val outputDir = activity!!.applicationContext.cacheDir val outputFile = File(outputDir.path + "/" + PARTITION + "_bundled.realm") // ensure all local changes have synced to the backend try { app.sync.getSession(config) .uploadAllLocalChanges(10000, TimeUnit.MILLISECONDS) } catch (e: InterruptedException) { e.printStackTrace() } // cannot write to file if it already exists. Delete the file if already there outputFile.delete() realm.writeCopyTo(outputFile) // search for this log line to find the location of the realm copy Log.i("EXAMPLE", "Wrote copy of realm to " + outputFile.absolutePath) // always close a realm when you're done using it realm.close() }) } else { Log.e("EXAMPLE", "Failed to authenticate: ${it.error}") } } writeCopyTo()在复制之前会自动将您的 Realm 压缩到尽可能小的大小。提示
同步 Realm 和仅限本地 Realm 之间的区别
上述示例使用
SyncConfiguration来配置同步 Realm。 要创建本地 Realm 的副本,请改为使用RealmConfiguration配置您的 Realm。
在生产应用程序中捆绑 Realm 文件
现在您已拥有包含初始数据的 Realm 副本,请将其与生产应用程序捆绑在一起。
搜索应用程序日志,找到刚刚创建的 Realm 文件副本的位置。
使用 Android Studio 窗口右下角的“设备文件浏览器”小部件,导航到该文件。
右键单击该文件并选择“另存为”。 导航到生产应用程序的
/<app name>/src/main/assets文件夹。 在此处保存 Realm 文件的副本。
提示
资产文件夹
如果您的应用程序尚不包含资产文件夹,您可以通过右键单击 Android Studio 中的顶级应用程序文件夹 ( <app name> ) 并选择菜单中的 New > Folder > Assets Folder来创建一个资产文件夹。
从捆绑的 Realm 文件中打开 Realm
现在您已拥有生产应用程序中包含的域副本,您需要添加代码来使用它。 配置域以从捆绑文件中打开域时,请使用assetFile()方法:
String appID = YOUR_APP_ID; // replace this with your App ID App app = new App(appID); Credentials anonymousCredentials = Credentials.anonymous(); app.loginAsync(anonymousCredentials, it -> { if (it.isSuccess()) { Log.v("EXAMPLE", "Successfully authenticated anonymously."); // asset file name should correspond to the name of the bundled file SyncConfiguration config = new SyncConfiguration.Builder( app.currentUser(), "PARTITION_YOU_WANT_TO_BUNDLE") .assetFile("example_bundled.realm") .build(); Realm realm = Realm.getInstance(config); Log.v("EXAMPLE", "Successfully opened bundled realm."); // read and write to the bundled realm as normal realm.executeTransactionAsync(transactionRealm -> { Frog frog = new Frog(new ObjectId(), "Asimov", 4, "red eyed tree frog", "Spike"); transactionRealm.insert(frog); expectation.fulfill(); }); } else { Log.e("EXAMPLE", "Failed to authenticate: " + it.getError().toString()); } });
val appID: String = YOUR_APP_ID // replace this with your App ID val app = App(appID) val anonymousCredentials = Credentials.anonymous() app.loginAsync(anonymousCredentials) { it: App.Result<User?> -> if (it.isSuccess) { Log.v("EXAMPLE", "Successfully authenticated anonymously.") // asset file name should correspond to the name of the bundled file val config = SyncConfiguration.Builder( app.currentUser(), "PARTITION_YOU_WANT_TO_BUNDLE") .assetFile("example_bundled.realm") .build() val realm: Realm = Realm.getInstance(config) Log.v("EXAMPLE", "Successfully opened bundled realm.") // read and write to the bundled realm as normal realm.executeTransactionAsync { transactionRealm: Realm -> val frog = Frog( ObjectId(), "Asimov", 4, "red eyed tree frog", "Spike" ) transactionRealm.insert(frog) expectation.fulfill() } } else { Log.e("EXAMPLE", "Failed to authenticate: ${it.error}") } }
提示
同步 Realm 和仅限本地 Realm 之间的区别
上述示例使用SyncConfiguration来配置同步 Realm。 要创建本地 Realm 的副本,请改为使用RealmConfiguration配置您的 Realm。