2011-12-20 7 views
3

У меня проблема с db4o на Android 3.0+, потому что получается, что при создании базы данных db4o по умолчанию используется часть apis сети. (Я наткнулся на этот пост: http://mavistechchannel.wordpress.com/2011/11/18/db4o-at-honeycomb-and-ice-cream-sandwich/)db4o на Android 3.0+ Проблема

Однако, я попытался сделать запросы на создание db async, но я думаю, что у меня возникает проблема вызова db, прежде чем он будет полностью создан, поскольку он блокирует БД. (И теперь я получаю ошибку блокировки) Есть ли способ сделать это синхронно? или, как минимум, подождать, пока он не будет закончен? Вот мой 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

Обновление: Это db4o bug has been fixed. Если вы получаете самые новые 8,1 бита, ошибка не должна возникать, и обходное решение является обсолютным:

Вы получаете исключение с блокировкой файлов при попытке получить базу данных? Правильно.

Хорошо, проблема в том, что вы не ожидаете завершения задачи async и просто запустите новый, если Db4oHelperAsync.oc имеет значение null. Вам в основном нужно подождать до завершения инициализации и только затем использовать переменную Db4oHelperAsync.oc. Итак, ваша в Java синхронизация земли.

Например, вы можете сделать это: синхронизировать доступ Db4oHelperAsync.oc. При запросе базы данных подождите, пока не будет установлена ​​переменная. Теперь, к сожалению, я не знаю точное поведение задачи async. Я предполагаю, что он вернет метод .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

+0

Спасибо за вашу помощь. Я не последовал этому точно, но достаточно близко. Это немного разочаровывает, что это нужно сделать, но, о, хорошо! – Hosemeyer

1

Спасибо за сообщение об этом выпуске, это серьезный забавный спойлер на Android. Когда создается новый файл базы данных db4o, db4o создает уникальную внутреннюю подпись, вызывая java.net.InetAddress.getLocalHost(). GetHostName(). Исключения не попадают в этот вызов. Мы найдем обходное решение для Android и опубликуем здесь и на наших форумах, когда это будет исправлено.

Обновление 9 фев 2012: Проблема была исправлена, и новые сборки находятся в сети. http://community.versant.com/Blogs/db4o/tabid/197/entryid/1057/Default.aspx

Смежные вопросы