Android 3.0+问题上的db4o

时间:2011-12-20 16:12:21

标签: android db4o

我在Android 3.0+上遇到了db4o的问题,因为事实证明在创建db4o数据库时,它默认使用一些网络apis。 (我偶然发现了这篇文章:http://mavistechchannel.wordpress.com/2011/11/18/db4o-at-honeycomb-and-ice-cream-sandwich/关于它)

但是,我试图让数据库创建请求异步,但我认为我在完全创建数据库之前遇到了调用数据库的问题,因为它锁定了数据库。 (我现在得到一个锁定错误)有什么方法可以做同步吗?或者,至少等到它完成了?这是我的db4o助手:

public class Db4oHelperAsync implements Constants{

private static final String USE_INTERNAL_MEMORY_FOR_DATABASE = "USE_INTERNAL_MEMORY_FOR_DATABASE";

private static ObjectContainer oc = null;
private Context context; 

/**       
 * @param ctx
 */

public Db4oHelperAsync(Context ctx) {
    context = ctx;
}

/**
 * Create, open and close the database
 */
public ObjectContainer db() {


    if (oc == null || oc.ext().isClosed()) {

        if (Utilities.getPreferences(context).getBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true)) {

            new GetDbFromInternalMemory().execute();

        } else {
            new GetDbFromSDCard().execute();
        }
        return oc;


    } else {

        return oc;

    }

}
/**
 * Configure the behavior of the database
 */

private EmbeddedConfiguration dbConfig() throws IOException {
    EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();

    configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).objectField("name").indexed(true);
    configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnUpdate(true);
    configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnActivate(true);
    configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnDelete(true);

    configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).objectField("name").indexed(true);
    configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnUpdate(true);
    configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnActivate(true);

    return configuration;
}

/**
 * Returns the path for the database location
 */

private String db4oDBFullPathInternal(Context ctx) {
    return ctx.getDir("data", 0) + "/" + "testapp.db4o";
}

private String db4oDBFullPathSdCard(Context ctx) {
    File path = new File(Environment.getExternalStorageDirectory(), ".testapp");
    if (!path.exists()) {
        path.mkdir();
    }
    return path + "/" + "testapp.db4o";
}

/**
 * Closes the database
 */

public void close() {
    if (oc != null)
        oc.close();
}

private class GetDbFromInternalMemory extends AsyncTask<Void, Void, ObjectContainer>{

    @Override
    protected ObjectContainer doInBackground(Void... params) {
        try {
            ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathInternal(context));
            CLog.v("USING INTERNAL MEMORY FOR DATABASE");
            return obj;

        } catch (Exception ie) {
            ie.printStackTrace();
            CLog.e(Db4oHelper.class.getName(), ie.toString());
            return null;
        }
    }

    @Override
    protected void onPostExecute(ObjectContainer result)
    {
        oc = result;
    }
}

private class GetDbFromSDCard extends AsyncTask<Void, Void, ObjectContainer>{

    @Override
    protected ObjectContainer doInBackground(Void... params) {
        try {

            ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathSdCard(context)); 
            CLog.v("USING SDCARD FOR DATABASE");
            SharedPreferences.Editor edit = Utilities.getPreferencesEditor(context);
            edit.putBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true);
            edit.commit();

            return obj;

        } catch (Exception ie) {
            ie.printStackTrace();
            CLog.e(Db4oHelper.class.getName(), ie.toString());
            return null;
        }
    }

    @Override
    protected void onPostExecute(ObjectContainer result)
    {
        oc = result;
    }
}
}

2 个答案:

答案 0 :(得分:2)

更新:此db4o bug has been fixed。如果您获得最新的8.1位,则不应出现错误,并且解决方法是无效的:

尝试获取数据库时出现文件锁定异常?右。

问题是你不是在等待异步任务完成,只是在Db4oHelperAsync.oc为空的情况下启动一个新任务。您基本上必须等到初始化完成后才使用Db4oHelperAsync.oc变量。所以你在Java同步中。

例如,您可以执行此操作:同步Db4oHelperAsync.oc访问权限。请求数据库等待,直到设置变量。现在不幸的是,我不知道异步任务的确切行为。我的猜测是它将在主活动上运行.onPostExecute()方法。这也意味着你不能只是等待它,因为这意味着你阻止了Activity-Thread,并且永远不会执行.onPostExecute()。

这是我试图做的草稿。我从未执行过或编译过它。它可能有同步问题。例如,当初始化失败时,它会将您的应用程序挂起在.db()调用上,因为它会永远等待。所以要非常小心并尝试改进它:

public class Db4oHelperAsync implements Constants{

    private static final String USE_INTERNAL_MEMORY_FOR_DATABASE = "USE_INTERNAL_MEMORY_FOR_DATABASE";

    private static ObjectContainer oc = null;
    private static final Object lock = new Object();
    private Context context; 

    /**       
     * @param ctx
     */

    public Db4oHelperAsync(Context ctx) {
        context = ctx;
    }

    /**
     * Create, open and close the database
     */
    public ObjectContainer db() {       
        synchronized(lock){
            if (oc == null || oc.ext().isClosed()) {
                if (Utilities.getPreferences(context).getBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true)) {
                    new GetDbFromInternalMemory().start();
                } else {
                    new GetDbFromSDCard().start();
                }
                while(oc==null){
                    this.wait()
                }
                return oc;
            } else {
                return oc;
            }
        }
    }
    /**
     * Configure the behavior of the database
     */

    private EmbeddedConfiguration dbConfig() throws IOException {
        EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();

        configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).objectField("name").indexed(true);
        configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnUpdate(true);
        configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnActivate(true);
        configuration.common().objectClass(PersistentObjectWithCascadeOnDelete.class).cascadeOnDelete(true);

        configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).objectField("name").indexed(true);
        configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnUpdate(true);
        configuration.common().objectClass(PersistentObjectWithoutCascadeOnDelete.class).cascadeOnActivate(true);

        return configuration;
    }

    /**
     * Returns the path for the database location
     */

    private String db4oDBFullPathInternal(Context ctx) {
        return ctx.getDir("data", 0) + "/" + "testapp.db4o";
    }

    private String db4oDBFullPathSdCard(Context ctx) {
        File path = new File(Environment.getExternalStorageDirectory(), ".testapp");
        if (!path.exists()) {
            path.mkdir();
        }
        return path + "/" + "testapp.db4o";
    }

    /**
     * Closes the database
     */

    public void close() {

        synchronized(lock){
            if (oc != null)
                oc.close();
        }
    }

    private class GetDbFromInternalMemory extends Thread{

        @Override
        protected void run() {
            try {
                ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathInternal(context));
                CLog.v("USING INTERNAL MEMORY FOR DATABASE");

                synchronized(Db4oHelperAsync.lock){
                    Db4oHelperAsync.oc = obj;
                    Db4oHelperAsync.lock.notifyAll()
                }
            } catch (Exception ie) {
                ie.printStackTrace();
                CLog.e(Db4oHelper.class.getName(), ie.toString());
            }
        }
    }

    private class GetDbFromSDCard extends Thread{

        @Override
        protected void run() {
            try {
                ObjectContainer obj = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPathSdCard(context)); 
                CLog.v("USING SDCARD FOR DATABASE");
                SharedPreferences.Editor edit = Utilities.getPreferencesEditor(context);
                edit.putBoolean(USE_INTERNAL_MEMORY_FOR_DATABASE, true);
                edit.commit();

                synchronized(Db4oHelperAsync.lock){
                    Db4oHelperAsync.oc = obj;
                    Db4oHelperAsync.lock.notifyAll()
                }
            } catch (Exception ie) {
                ie.printStackTrace();
                CLog.e(Db4oHelper.class.getName(), ie.toString());
            }
        }
    }
}

P.S。将此问题作为错误添加到db4o:http://tracker.db4o.com/browse/COR-2269

答案 1 :(得分:1)

感谢您发布此问题,这对Android来说是一个非常有趣的破坏者。 创建新的db4o数据库文件时,db4o通过调用java.net.InetAddress.getLocalHost()。getHostName()生成其唯一的内部签名。此次通话中未包含例外情况。我们将找到针对Android的解决方法,并在此处以及在修复此问题后发布到我们的论坛。

2012年2月9日更新: 该问题已得到修复,新版本已在线。 http://community.versant.com/Blogs/db4o/tabid/197/entryid/1057/Default.aspx