From 0b1b42f4e4d7f3a1ff9ebbf8b82d09d7345ac26c Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Tue, 25 Oct 2016 11:05:00 +0200 Subject: [PATCH 1/3] Replaced Context with ContextWrapper --- .../src/main/java/io/realm/BaseRealm.java | 5 +- .../src/main/java/io/realm/Realm.java | 5 +- .../java/io/realm/RealmConfiguration.java | 10 ++-- .../internal/android/ContextWrapper.java | 47 +++++++++++++++++++ .../java/io/realm/SyncConfiguration.java | 5 +- 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java diff --git a/realm/realm-library/src/main/java/io/realm/BaseRealm.java b/realm/realm-library/src/main/java/io/realm/BaseRealm.java index 345c8a6735..7edf88242c 100644 --- a/realm/realm-library/src/main/java/io/realm/BaseRealm.java +++ b/realm/realm-library/src/main/java/io/realm/BaseRealm.java @@ -37,6 +37,7 @@ import io.realm.internal.Table; import io.realm.internal.UncheckedRow; import io.realm.internal.Util; +import io.realm.internal.android.ContextWrapper; import io.realm.internal.async.RealmThreadPoolExecutor; import io.realm.log.RealmLog; import io.realm.internal.ObjectServerFacade; @@ -60,8 +61,8 @@ abstract class BaseRealm implements Closeable { private static final String NOT_IN_TRANSACTION_MESSAGE = "Changing Realm data can only be done from inside a transaction."; - // Thread pool for all async operations (Query & transaction) - volatile static Context applicationContext; + // Android ContextWrapper. Must be initialized by calling `Realm.init()` before calling any other API's. + volatile static ContextWrapper contextWrapper; // Thread pool for all async operations (Query & transaction) static final RealmThreadPoolExecutor asyncTaskExecutor = RealmThreadPoolExecutor.newDefaultExecutor(); diff --git a/realm/realm-library/src/main/java/io/realm/Realm.java b/realm/realm-library/src/main/java/io/realm/Realm.java index 632ce353f0..5e3535b3d8 100644 --- a/realm/realm-library/src/main/java/io/realm/Realm.java +++ b/realm/realm-library/src/main/java/io/realm/Realm.java @@ -57,6 +57,7 @@ import io.realm.internal.RealmProxyMediator; import io.realm.internal.SharedRealm; import io.realm.internal.Table; +import io.realm.internal.android.ContextWrapper; import io.realm.internal.async.RealmAsyncTaskImpl; import io.realm.log.AndroidLogger; import io.realm.log.RealmLog; @@ -183,7 +184,7 @@ public Observable asObservable() { * @see #getDefaultInstance() */ public static synchronized void init(Context context) { - if (BaseRealm.applicationContext == null) { + if (BaseRealm.contextWrapper == null) { if (context == null) { throw new IllegalArgumentException("Non-null context required."); } @@ -191,7 +192,7 @@ public static synchronized void init(Context context) { RealmLog.add(io.realm.BuildConfig.DEBUG ? new AndroidLogger(Log.DEBUG) : new AndroidLogger(Log.WARN)); defaultConfiguration = new RealmConfiguration.Builder(context).build(); ObjectServerFacade.getSyncFacadeIfPossible().init(context); - BaseRealm.applicationContext = context.getApplicationContext(); + BaseRealm.contextWrapper = new ContextWrapper(context.getApplicationContext()); SharedRealm.initialize(new File(context.getFilesDir(), ".realm.temp")); } } diff --git a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java index 06775f7abc..725b855c27 100644 --- a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java +++ b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java @@ -35,6 +35,7 @@ import io.realm.internal.RealmCore; import io.realm.internal.RealmProxyMediator; import io.realm.internal.SharedRealm; +import io.realm.internal.android.ContextWrapper; import io.realm.internal.modules.CompositeMediator; import io.realm.internal.modules.FilterableMediator; import io.realm.rx.RealmObservableFactory; @@ -187,7 +188,7 @@ boolean hasAssetFile() { * @throws IOException if copying the file fails. */ InputStream getAssetFile() throws IOException { - return BaseRealm.applicationContext.getAssets().open(assetFilePath); + return BaseRealm.contextWrapper.getAssets().open(assetFilePath); } /** @@ -391,19 +392,18 @@ public static class Builder { * change depending on vendor implementations of Android. */ public Builder() { - this(BaseRealm.applicationContext); + this(BaseRealm.contextWrapper); } - Builder(Context context) { + Builder(ContextWrapper context) { if (context == null) { throw new IllegalStateException("Call `Realm.init(Context)` before creating a RealmConfiguration"); } - RealmCore.loadLibrary(context); initializeBuilder(context); } // Setup builder in its initial state - private void initializeBuilder(Context context) { + private void initializeBuilder(ContextWrapper context) { this.directory = context.getFilesDir(); this.fileName = Realm.DEFAULT_REALM_NAME; this.key = null; diff --git a/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java new file mode 100644 index 0000000000..d9771da8f3 --- /dev/null +++ b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.realm.internal.android; + +import android.content.Context; +import android.content.res.AssetManager; + +import java.io.File; + +/** + * This class wraps the Android Context class by extracting all relevant information from the context. + * This means we only need the Context when {@link io.realm.Realm#init(Context)} is called and can + * dispose of it afterwards. + * + * This should also make Realm play nicer with Instant Run: + */ +public class ContextWrapper { + + private final AssetManager assetManager; + private File filesDir; + + public ContextWrapper(Context applicationContext) { + assetManager = applicationContext.getAssets(); + filesDir = applicationContext.getFilesDir(); + } + + public AssetManager getAssets() { + return assetManager; + } + + public File getFilesDir() { + return filesDir; + } +} diff --git a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java index e930e14c27..7e395b9cc5 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java +++ b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java @@ -34,6 +34,7 @@ import io.realm.exceptions.RealmException; import io.realm.internal.RealmProxyMediator; import io.realm.internal.SharedRealm; +import io.realm.internal.android.ContextWrapper; import io.realm.internal.syncpolicy.AutomaticSyncPolicy; import io.realm.internal.syncpolicy.SyncPolicy; import io.realm.rx.RealmObservableFactory; @@ -277,10 +278,10 @@ public static final class Builder { * @see User#isValid() */ public Builder(User user, String uri) { - this(BaseRealm.applicationContext, user, uri); + this(BaseRealm.contextWrapper, user, uri); } - Builder(Context context, User user, String url) { + Builder(ContextWrapper context, User user, String url) { if (context == null) { throw new IllegalStateException("Call `Realm.init(Context)` before creating a SyncConfiguration"); } From 8b2eba833d6e35dee1e3f83de0a2eae2ed053441 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 26 Oct 2016 09:36:12 +0200 Subject: [PATCH 2/3] Generalise a bit more, so the ContextWrapper API is less Android specific. --- .../src/main/java/io/realm/Realm.java | 8 +++---- .../java/io/realm/RealmConfiguration.java | 7 +++--- .../internal/android/ContextWrapper.java | 23 +++++++++++++------ .../java/io/realm/SyncConfiguration.java | 2 +- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/realm/realm-library/src/main/java/io/realm/Realm.java b/realm/realm-library/src/main/java/io/realm/Realm.java index 636018e6dd..00fb13e3b9 100644 --- a/realm/realm-library/src/main/java/io/realm/Realm.java +++ b/realm/realm-library/src/main/java/io/realm/Realm.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.Build; import android.util.JsonReader; -import android.util.Log; import org.json.JSONArray; import org.json.JSONException; @@ -188,10 +187,11 @@ public static synchronized void init(Context context) { throw new IllegalArgumentException("Non-null context required."); } RealmCore.loadLibrary(context); - defaultConfiguration = new RealmConfiguration.Builder(context).build(); + ContextWrapper wrapper = new ContextWrapper(context); + defaultConfiguration = new RealmConfiguration.Builder(wrapper).build(); ObjectServerFacade.getSyncFacadeIfPossible().init(context); - BaseRealm.contextWrapper = new ContextWrapper(context.getApplicationContext()); - SharedRealm.initialize(new File(context.getFilesDir(), ".realm.temp")); + BaseRealm.contextWrapper = wrapper; + SharedRealm.initialize(new File(wrapper.getDefaultRealmFileFolder(), ".realm.temp")); } } diff --git a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java index 725b855c27..5017861150 100644 --- a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java +++ b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java @@ -16,7 +16,6 @@ package io.realm; -import android.content.Context; import android.text.TextUtils; import java.io.File; @@ -32,7 +31,6 @@ import io.realm.annotations.RealmModule; import io.realm.exceptions.RealmException; import io.realm.exceptions.RealmFileException; -import io.realm.internal.RealmCore; import io.realm.internal.RealmProxyMediator; import io.realm.internal.SharedRealm; import io.realm.internal.android.ContextWrapper; @@ -188,7 +186,8 @@ boolean hasAssetFile() { * @throws IOException if copying the file fails. */ InputStream getAssetFile() throws IOException { - return BaseRealm.contextWrapper.getAssets().open(assetFilePath); + return BaseRealm.contextWrapper.getAsset(assetFilePath); + } /** @@ -404,7 +403,7 @@ public Builder() { // Setup builder in its initial state private void initializeBuilder(ContextWrapper context) { - this.directory = context.getFilesDir(); + this.directory = context.getDefaultRealmFileFolder(); this.fileName = Realm.DEFAULT_REALM_NAME; this.key = null; this.schemaVersion = 0; diff --git a/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java index d9771da8f3..9b2f44db69 100644 --- a/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java +++ b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java @@ -19,13 +19,15 @@ import android.content.res.AssetManager; import java.io.File; +import java.io.IOException; +import java.io.InputStream; /** * This class wraps the Android Context class by extracting all relevant information from the context. - * This means we only need the Context when {@link io.realm.Realm#init(Context)} is called and can - * dispose of it afterwards. + * This means we only need the Context when {@link io.realm.Realm#init(Context)} is called, but we don't + * need to hold onto it. * - * This should also make Realm play nicer with Instant Run: + * This should also make Realm play nicer with Instant Run. */ public class ContextWrapper { @@ -37,11 +39,18 @@ public ContextWrapper(Context applicationContext) { filesDir = applicationContext.getFilesDir(); } - public AssetManager getAssets() { - return assetManager; + /** + * Returns the default location for Realm files. + */ + public File getDefaultRealmFileFolder() { + return filesDir; } - public File getFilesDir() { - return filesDir; + /** + * Returns an asset specified by a given path. + * On Android this path is assumed to be a path under the {@code assets} folder. + */ + public InputStream getAsset(String assetFilePath) throws IOException { + return assetManager.open(assetFilePath); } } diff --git a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java index f2f7572575..43800a91f6 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java +++ b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java @@ -377,7 +377,7 @@ public Builder name(String filename) { * this means that multiple users can save their Realms on disk without the risk of them overwriting * each other files. *

- * The default location is {@code context.getFilesDir()}. + * The default location is {@code context.getDefaultRealmFileFolder()}. * * @param directory directory on disk where the Realm file can be saved. * @throws IllegalArgumentException if the directory is not valid. From 034a37b691d58dd4742999d16eb377073a497b1f Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Wed, 26 Oct 2016 09:44:14 +0200 Subject: [PATCH 3/3] Consistent naming + Fix SyncConfiguration --- realm/realm-library/src/main/java/io/realm/Realm.java | 2 +- .../src/main/java/io/realm/RealmConfiguration.java | 2 +- .../main/java/io/realm/internal/android/ContextWrapper.java | 2 +- .../src/objectServer/java/io/realm/SyncConfiguration.java | 6 ++---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/realm/realm-library/src/main/java/io/realm/Realm.java b/realm/realm-library/src/main/java/io/realm/Realm.java index 00fb13e3b9..5f093dcd5e 100644 --- a/realm/realm-library/src/main/java/io/realm/Realm.java +++ b/realm/realm-library/src/main/java/io/realm/Realm.java @@ -191,7 +191,7 @@ public static synchronized void init(Context context) { defaultConfiguration = new RealmConfiguration.Builder(wrapper).build(); ObjectServerFacade.getSyncFacadeIfPossible().init(context); BaseRealm.contextWrapper = wrapper; - SharedRealm.initialize(new File(wrapper.getDefaultRealmFileFolder(), ".realm.temp")); + SharedRealm.initialize(new File(wrapper.getDefaultRealmFileDirectory(), ".realm.temp")); } } diff --git a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java index 5017861150..08de79cc96 100644 --- a/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java +++ b/realm/realm-library/src/main/java/io/realm/RealmConfiguration.java @@ -403,7 +403,7 @@ public Builder() { // Setup builder in its initial state private void initializeBuilder(ContextWrapper context) { - this.directory = context.getDefaultRealmFileFolder(); + this.directory = context.getDefaultRealmFileDirectory(); this.fileName = Realm.DEFAULT_REALM_NAME; this.key = null; this.schemaVersion = 0; diff --git a/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java index 9b2f44db69..8e47313f7b 100644 --- a/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java +++ b/realm/realm-library/src/main/java/io/realm/internal/android/ContextWrapper.java @@ -42,7 +42,7 @@ public ContextWrapper(Context applicationContext) { /** * Returns the default location for Realm files. */ - public File getDefaultRealmFileFolder() { + public File getDefaultRealmFileDirectory() { return filesDir; } diff --git a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java index 43800a91f6..085edd909f 100644 --- a/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java +++ b/realm/realm-library/src/objectServer/java/io/realm/SyncConfiguration.java @@ -16,8 +16,6 @@ package io.realm; -import android.content.Context; - import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -285,7 +283,7 @@ public Builder(SyncUser user, String uri) { if (context == null) { throw new IllegalStateException("Call `Realm.init(Context)` before creating a SyncConfiguration"); } - this.defaultFolder = new File(context.getFilesDir(), "realm-object-server"); + this.defaultFolder = new File(context.getDefaultRealmFileDirectory(), "realm-object-server"); if (Realm.getDefaultModule() != null) { this.modules.add(Realm.getDefaultModule()); } @@ -377,7 +375,7 @@ public Builder name(String filename) { * this means that multiple users can save their Realms on disk without the risk of them overwriting * each other files. *

- * The default location is {@code context.getDefaultRealmFileFolder()}. + * The default location is {@code context.getFilesDir()}. * * @param directory directory on disk where the Realm file can be saved. * @throws IllegalArgumentException if the directory is not valid.