Copying embedded Realm files using AssetManager from JNI
This commit is contained in:
parent
15953e13b9
commit
895c6a3056
|
@ -1,5 +1,6 @@
|
|||
package io.realm.react;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
|
@ -10,8 +11,8 @@ import java.io.IOException;
|
|||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
@ -25,6 +26,11 @@ public class RealmReactModule extends ReactContextBaseJavaModule {
|
|||
private static boolean sentAnalytics = false;
|
||||
|
||||
private AndroidWebServer webServer;
|
||||
// used to create a native AssetManager in C++ in order to load file from APK
|
||||
// Note: We keep a VM reference to the assetManager to prevent its being
|
||||
// garbage collected while the native object is in use.
|
||||
//http://developer.android.com/ndk/reference/group___asset.html#gadfd6537af41577735bcaee52120127f4
|
||||
private final AssetManager assetManager;
|
||||
|
||||
static {
|
||||
SoLoader.loadLibrary("realmreact");
|
||||
|
@ -33,8 +39,7 @@ public class RealmReactModule extends ReactContextBaseJavaModule {
|
|||
public RealmReactModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
// copy any embedded Realm files from assets to the internal storage
|
||||
RealmReactPackage.copyRealmsFromAsset(reactContext);
|
||||
assetManager = reactContext.getResources().getAssets();
|
||||
|
||||
String fileDir;
|
||||
try {
|
||||
|
@ -43,7 +48,7 @@ public class RealmReactModule extends ReactContextBaseJavaModule {
|
|||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
setDefaultRealmFileDirectory(fileDir);
|
||||
setDefaultRealmFileDirectory(fileDir, assetManager);
|
||||
|
||||
// Attempt to send analytics info only once, and only if allowed to do so.
|
||||
if (!sentAnalytics && RealmAnalytics.shouldExecute()) {
|
||||
|
@ -170,7 +175,7 @@ public class RealmReactModule extends ReactContextBaseJavaModule {
|
|||
private native void clearContextInjectedFlag();
|
||||
|
||||
// fileDir: path of the internal storage of the application
|
||||
private native void setDefaultRealmFileDirectory(String fileDir);
|
||||
private native void setDefaultRealmFileDirectory(String fileDir, AssetManager assets);
|
||||
|
||||
// responsible for creating the rpcServer that will accept the chrome Websocket command
|
||||
private native long setupChromeDebugModeRealmJsContext();
|
||||
|
|
|
@ -1,26 +1,15 @@
|
|||
package io.realm.react;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class RealmReactPackage implements ReactPackage {
|
||||
private final static String REALM_FILE_FILTER = ".realm";
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Collections.<NativeModule>singletonList(new RealmReactModule(reactContext));
|
||||
|
@ -35,65 +24,4 @@ public class RealmReactPackage implements ReactPackage {
|
|||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static void copyRealmsFromAsset(final Context context) {
|
||||
try {
|
||||
final AssetManager assets = context.getAssets();
|
||||
String[] list = assets.list("");
|
||||
File file = null;
|
||||
for (String asset : list) {
|
||||
//FIXME instead check if the file exist with the .management extension
|
||||
// ex: dates-v3.realm.management
|
||||
if (asset.endsWith(REALM_FILE_FILTER) && !(file = new File(context.getFilesDir(), asset)).exists()) {
|
||||
copyFromAsset(assets, asset, file);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyFromAsset(AssetManager assets, String realmFile, File destination) {
|
||||
|
||||
ReadableByteChannel inChannel = null;
|
||||
FileChannel outChannel = null;
|
||||
boolean newFileCreated = false;
|
||||
try {
|
||||
newFileCreated = destination.createNewFile();
|
||||
inChannel = Channels.newChannel(assets.open(realmFile));
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(destination);
|
||||
outChannel = fileOutputStream.getChannel();
|
||||
|
||||
long offset = 0;
|
||||
long quantum = 1024 * 1024;
|
||||
long count;
|
||||
while ((count = outChannel.transferFrom(inChannel, offset, quantum)) > 0) {
|
||||
offset += count;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// try to remove the empty file created
|
||||
if (newFileCreated) {
|
||||
destination.delete();
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (inChannel != null) {
|
||||
try {
|
||||
inChannel.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (outChannel != null) {
|
||||
try {
|
||||
outChannel.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,5 +16,6 @@ APP_CPPFLAGS += -DREALM_HAVE_CONFIG
|
|||
# Make sure every shared lib includes a .note.gnu.build-id header
|
||||
APP_LDFLAGS := -Wl,--build-id
|
||||
APP_LDFLAGS += -llog
|
||||
APP_LDFLAGS += -landroid
|
||||
|
||||
NDK_TOOLCHAIN_VERSION := 4.9
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
|
||||
#include "io_realm_react_RealmReactModule.h"
|
||||
#include "rpc.hpp"
|
||||
|
@ -29,10 +30,17 @@ static RPCServer *s_rpc_server;
|
|||
extern bool realmContextInjected;
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactModule_setDefaultRealmFileDirectory
|
||||
(JNIEnv *env, jclass, jstring fileDir)
|
||||
(JNIEnv *env, jclass, jstring fileDir, jobject javaAssetManager)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "JSRealm", "setDefaultRealmFileDirectory");
|
||||
|
||||
// Get the assetManager in case we want to copy files from the APK (assets)
|
||||
AAssetManager *assetManager = AAssetManager_fromJava(env, javaAssetManager);
|
||||
if (assetManager == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "JSRealm", "Error loading the AssetManager");
|
||||
}
|
||||
realm::set_asset_manager(assetManager);
|
||||
|
||||
// Setting the internal storage path for the application
|
||||
const char* strFileDir = env->GetStringUTFChars(fileDir, NULL);
|
||||
realm::set_default_realm_file_directory(strFileDir);
|
||||
|
|
|
@ -13,7 +13,7 @@ extern "C" {
|
|||
* Method: setDefaultRealmFileDirectory
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_io_realm_react_RealmReactModule_setDefaultRealmFileDirectory
|
||||
(JNIEnv *, jclass, jstring);
|
||||
(JNIEnv *, jclass, jstring, jobject);
|
||||
|
||||
/*
|
||||
* Class: io_realm_react_RealmReactModule
|
||||
|
|
|
@ -18,9 +18,23 @@
|
|||
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#include <android/log.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include "../platform.hpp"
|
||||
|
||||
#define REALM_FILE_FILTER ".realm"
|
||||
#define REALM_FILE_FILTER_LEN strlen(REALM_FILE_FILTER)
|
||||
AAssetManager* androidAssetManager;
|
||||
inline bool isRealmFile(const char *str)
|
||||
{
|
||||
size_t lenstr = strlen(str);
|
||||
if (REALM_FILE_FILTER_LEN > lenstr)
|
||||
return false;
|
||||
return strncmp(str + lenstr - REALM_FILE_FILTER_LEN, REALM_FILE_FILTER, REALM_FILE_FILTER_LEN) == 0;
|
||||
}
|
||||
|
||||
std::string s_default_realm_directory;
|
||||
|
||||
namespace realm {
|
||||
|
@ -30,13 +44,50 @@ namespace realm {
|
|||
s_default_realm_directory = dir;
|
||||
}
|
||||
|
||||
void set_asset_manager(AAssetManager* assetManager)
|
||||
{
|
||||
androidAssetManager = assetManager;
|
||||
}
|
||||
|
||||
std::string default_realm_file_directory()
|
||||
{
|
||||
return s_default_realm_directory;
|
||||
}
|
||||
|
||||
void ensure_directory_exists_for_file(const std::string &fileName)
|
||||
void ensure_directory_exists_for_file(const std::string &file)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void copy_bundled_realm_files()
|
||||
{
|
||||
AAssetDir* assetDir = AAssetManager_openDir(androidAssetManager, "");
|
||||
|
||||
const char* filename = (const char*)NULL;
|
||||
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL)
|
||||
{
|
||||
if (isRealmFile(filename))
|
||||
{
|
||||
AAsset* asset = AAssetManager_open(androidAssetManager, filename, AASSET_MODE_STREAMING);
|
||||
|
||||
char buf[BUFSIZ];
|
||||
int nb_read = 0;
|
||||
|
||||
const char* destFilename = (s_default_realm_directory + '/' + filename).c_str();
|
||||
if(access(destFilename, F_OK ) == -1 ) {
|
||||
// file doesn't exist, copy
|
||||
FILE* out = fopen(destFilename, "w");
|
||||
while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
|
||||
{
|
||||
fwrite(buf, nb_read, 1, out);
|
||||
}
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
AAsset_close(asset);
|
||||
}
|
||||
}
|
||||
AAssetDir_close(assetDir);
|
||||
}
|
||||
|
||||
void remove_realm_files_from_directory(const std::string &directory)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <android/asset_manager.h>
|
||||
|
||||
extern std::string s_default_realm_directory;
|
||||
|
||||
|
@ -31,6 +32,9 @@ namespace realm {
|
|||
// set the directory where realm files should be stored
|
||||
void set_default_realm_file_directory(std::string dir);
|
||||
|
||||
// set the AssetManager used to access bundled files within the APK
|
||||
void set_asset_manager(AAssetManager* assetManager);
|
||||
|
||||
// return the directory in which realm files can/should be written to
|
||||
std::string default_realm_file_directory();
|
||||
|
||||
|
|
|
@ -474,15 +474,14 @@ module.exports = BaseTest.extend({
|
|||
Realm.copyBundledRealmFiles();
|
||||
|
||||
var DateSchema = {
|
||||
name: 'MyDate',
|
||||
name: 'Date',
|
||||
properties: {
|
||||
currentDate: 'date'
|
||||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({path: 'dates-v4.realm', schema: [DateSchema]});
|
||||
var realm = new Realm({path: 'dates-v3.realm', schema: [DateSchema]});
|
||||
|
||||
TestCase.assertEqual(realm.objects('MyDate').length, 1);
|
||||
// TestCase.assertEqual(realm.objects('Date')[0].currentDate.getTime(), 1462500087955);
|
||||
TestCase.assertEqual(realm.objects('Date')[0].currentDate.getTime(), 819867600000);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue