2016-07-24 15 views
1

Ich arbeite derzeit an einem Android-Projekt, das eine Menge Kommunikation mit der SQLite-Datenbank durchführt. Ich versuche auch, ein MVP-Framework innerhalb der App zu implementieren.Umgang mit SQLite Singleton-Instanzen und ihre Kontextabhängigkeit in Android

Meine aktuelle Implementierung der Singleton-Instanz ähnelt der folgenden. (Von diesem Posten genommen: https://github.com/codepath/android_guides/wiki/Local-Databases-with-SQLiteOpenHelper)

public class PostsDatabaseHelper extends SQLiteOpenHelper { 
    private static PostsDatabaseHelper sInstance; 

    public static synchronized PostsDatabaseHelper getInstance(Context context) { 
    if (sInstance == null) { 
     sInstance = new PostsDatabaseHelper(context.getApplicationContext()); 
    } 
    return sInstance; 
    } 

    private PostsDatabaseHelper(Context context) { 
    super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 
} 

Mit dem vorhandenen Code oben, ich nenne die getInstance Methode in mehreren Presenter Klassen, in jedem von ihnen das Context-Objekt, das auf der Aktivität/Fragment übergeben wurde. Die Context-Objekte können über mehrere Klassen hinweg übergeben werden.

Anstelle des obigen Codes dachte ich daran, den databaseHelper nur einmal zu Beginn der Anwendung zu instanziieren, und dann verweisen alle Referenzen auf eine Variante der getInstance-Methode ohne die Kontextabhängigkeit.

BEARBEITEN: Mein Hauptziel ist es, so weit wie möglich das Vorhandensein des Context-Objekts in den Presenter-Klassen zu entfernen, um den Code "sauberer" zu machen. Da alle Aufrufe von getInstance den gleichen Kontexttyp (den Kontext der Anwendung und keinen aktivitätsspezifischen Kontext) bereitstellen/einfügen, wird das Kontextobjekt nicht als Argument angezeigt.

public class PostsDatabaseHelper extends SQLiteOpenHelper { 
    private static PostsDatabaseHelper sInstance; 

    // called by all other classes 
    public static synchronized PostsDatabaseHelper getInstance() { 
    if (sInstance == null) { 
     //throw error 
    } 
    return sInstance; 
    } 

    // only called once at the start of the Application 
    public static void instantiateInstance(Context context){ 
    sInstance = new PostsDatabaseHelper(context.getApplicationContext()); 
    } 

    private PostsDatabaseHelper(Context context) { 
    super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 
} 

Was ich gerne wissen würde, ist, gibt es irgendwelche Nachteile für diesen Ansatz? Vielen Dank!

Antwort

2

Sie handeln träge Initialisierung für statische Initialisierung.

Im Allgemeinen kann die faule Initialisierung die Kosten der Initialisierung über die Lebensdauer einer Anwendung amortisieren. In diesem Fall scheint es aus zwei Gründen weniger wichtig zu sein:

  1. Es ist fast sicher, dass Sie diese Datenbank benötigen. Es scheint unwahrscheinlich, dass Sie die Initialisierung deaktivieren, so dass Sie es möglicherweise gar nicht erst machen müssen.
  2. Das Android-Framework garantiert, dass der DBHelper-Konstruktor aus dem UI-Thread ausgeführt werden kann: Es ist nicht, was die Zeit braucht. Die Sache, die sich die Zeit nimmt, ist der erste Anruf an getWriteableDatabase. Die faule Schöpfung des Helfers vollbringt fast nichts.

Sie betrachten könnte der Code noch weniger verworren machen, durch die DB in der Anwendung wie folgt initialisiert:

public class DBDrivenApp extends Application implements DBProvider { 

    // ... 

    private PostsDatabaseHelper db; 

    // ... 

    @Override 
    public void onCreate() { 
    super.onCreate(); 
    db = new PostsDatabaseHelper(this); 
    } 

    @Override 
    public PostsDatabaseHelper getDB() { return db; } 

    // ... 
} 

... und, besser noch, einen IoC-Framework verwenden, wie Dagger2, zu injizieren Sie die Datenbankinstanz, damit Sie sie beim Testen nachahmen können.

+0

Hallo Blake, danke für die ausführliche Antwort. Ich stimme Ihnen zu, dass der Aufwand für die Initialisierung vernachlässigbar ist, wenn Sie die lazy-Initialisierung verwenden. Mein Hauptziel ist es, das Vorhandensein des 'Context'-Objekts in den Presenter-Klassen wirklich zu reduzieren, um den Code' sauberer 'zu machen. Da alle Aufrufe der Methode getInstance tatsächlich denselben Anwendungskontext bereitstellen, sehe ich keinen Grund, warum das Context-Objekt überhaupt als Argument für nachfolgende Aufrufe übergeben werden sollte. Wenn das Context-Objekt, das übergeben wurde, ein aktivitätsspezifischer Kontext ist, ist es sinnvoll, es als Argument zu verwenden. –

+0

Ja. Ich denke, ich stimme all dem zu. Wenn Sie den von mir vorgeschlagenen Code verwenden, müssen Sie den DBProvider an Ihren Presenter übergeben (was übrigens ein Kontext und eine Anwendung ist). Wenn dein Presenter Sachen von der DB bekommen muss, scheint das vernünftig ... –

+0

...oder, btw, wenn das zu alt für dich ist, könntest du etwas mehr iOS-Stil machen, indem du den DBHelper in etwas einwickelst, das auf einem Event-Bus horcht. Dann müssten Sie den DBProvider nicht einmal an den Presenter übergeben. –