Static eller en instans för varje activity?

Diskussion i 'Frågor, support och diskussion' startad av Adam2, 9 jul 2011.

  1. Adam2

    Adam2 Adult Droid Medlem

    Blev medlem:
    26 jul 2010
    Inlägg:
    732
    Mottagna gillanden:
    55

    MINA ENHETER

    Har börjat intressera mig för minnesoptimering (om än på väldigt basic nivå) och försöker lista ut vilket av följande som är bäst ur både minnessynpunkt och andra aspekter (lätt att koda, effektiv exekvering etc).

    Hela frågan går ut på att jag behöver ha tillgång till flertalet "hjälpklasser" från flera Activities. Jag har då två alternativ som jag ser det.

    Alternativ 1:
    I MainActivity gör jag följande

    Kod:
    public class MainActivity extends Activity {
        
        public static DatabaseHelper dbHelper;
        
        [USER=4487]OvE[/USER]rride
        public void onCreate(Bundle savedInstanceState) {
            ....
            dbHelper = new DatabaseHelper(this);
            ....
        }
    }
    Därefter nyttjar jag denna i andra klasser, tex:

    Kod:
    public class AnotherActivity extends Activity {
        
        public void doSomething() {
            ....
            MainActivity.dbHelper.getSomeData();
            ....
        }
    }
    Alternativ 2:
    I MainActivity skapas en private instance

    Kod:
    public class MainActivity extends Activity {
        
        private DatabaseHelper dbHelper;
        
        [USER=4487]OvE[/USER]rride
        public void onCreate(Bundle savedInstanceState) {
            dbHelper = new DatabaseHelper(this);
        }
    }
    I andra klasser gör jag likadant

    Kod:
    public class AnotherActivity extends Activity {
    
        private databaseHelper dbHelper;
    
        public AnotherActivity (Context c) {
            dbHelper = new databaseHelper(c);
        }
        
        public void doSomething() {
            dbHelper.getSomeData();
        }
    }
    Vad är bäst? Mest minneseffektivt? Lättast att jobba med?

    EDIT: Om man kör på alternativ 2, vad kan man göra när den activityn avslutas? Kan man explicit döda dbHelper eller ska man bara låta den vara och hoppas på att GC gör jobbet?
     
    Last edited: 9 jul 2011
  2. Durza007

    Durza007 Baby Droid Medlem

    Blev medlem:
    14 feb 2010
    Inlägg:
    28
    Mottagna gillanden:
    0

    MINA ENHETER

    I ditt första alternativ så kommer du att läcka din "MainActivity" eftersom databaseHelper som är statisk håller på en referens till din aktivitet. Din aktivitet (och allt som den i sin tur håller på) kommer då att ligga kvar i minnet. Du bör i konstruktorn i stället för "this" använda "getApplicationContext()" för att undvika en detta.
     
  3. foobar17

    foobar17 Kid Droid Medlem

    Blev medlem:
    12 jun 2009
    Inlägg:
    56
    Mottagna gillanden:
    11

    MINA ENHETER

    Skulle avråda från static object, eftersom det försvårar för både GC och livscykel hanteringen.

    Bättre är att utnyttja applikations klassen (skapa en om du inte redan har en), dvs subklass till android.app.Application. Detta objekt lever längst av alla objekt i din app. Låt app objektet hålla reda på alla andra objekt (istf singletons). Fördelen är att app objektet har livscyckel metoder du kan använda för att synka dina objekt.
     
    Adam2 gillar detta.
  4. mach

    mach Youth Droid Medlem

    Blev medlem:
    29 apr 2010
    Inlägg:
    115
    Mottagna gillanden:
    4

    MINA ENHETER

    Håller helt med. Om en aktivitet inte är längst fram i vy-stacken kan du faktiskt inte förutsätta att den inte blir terminerad av Android för att spara minne. Det är en feature, inte en bugg :)
     
  5. ViLANDER

    ViLANDER Senior Droid Medlem

    Blev medlem:
    12 dec 2009
    Inlägg:
    1 594
    Mottagna gillanden:
    172

    MINA ENHETER

    Jag instämmer här! :)
     
  6. Adam2

    Adam2 Adult Droid Medlem

    Blev medlem:
    26 jul 2010
    Inlägg:
    732
    Mottagna gillanden:
    55

    MINA ENHETER

    Låter vettigt, ska undersöka detta.

    Detta har jag också lärt mig, undvik static så mycket det går. Frågan var bara; när blir 1 static bättre än x "vanliga" referenser..!?

    Ang. Application och singletons; menar du att jag ska ha min
    Kod:
    databaseHelper dbHelper = new databaseHelper(context);
    i denna Applicationklass?
    Någon får gärna förklara mer om detta eller länka till bra sidor (ska självklart googla själv men inte nu :P)

    mach och ViLANDER; Det köper jag rakt av. Just därför man använder static väl? (Generellt, inser att det inte är den bästa lösningen i detta fall)
     
  7. nadam

    nadam Youth Droid Medlem

    Blev medlem:
    10 feb 2010
    Inlägg:
    182
    Mottagna gillanden:
    31
    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact

    MINA ENHETER

    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact
    Det går bra att använda singletons och andra statics i Android så länge man gör det på rätt sätt. Om en singleton behöver context så kan den hämta application context på följande sätt:

    Kod:
        public static DatabaseHelper getInstance(Context caller) {
            if (sInstance == null) {
                sInstance = new DatabaseHelper(caller.getApplicationContext());
            }
            return sInstance;
        }
    Du kan sedan anropa denna i onCreate() som i ditt alternativ 2 istället för att göra new.

    Om du använder trådar som kan anropa metoden samtidigt som onCreate() så behöver du också synkronisera, men det är lite långsökt i en Android-app.
     
    Adam2 gillar detta.
  8. Adam2

    Adam2 Adult Droid Medlem

    Blev medlem:
    26 jul 2010
    Inlägg:
    732
    Mottagna gillanden:
    55

    MINA ENHETER

    Tack för ditt svar!
    Det verkar dock vara ganska många (tex på SO) som föredrar en Applicationklass före Singleton. Se tex accepted answers här och här.

    EDIT: Eller var är det tänkt att den kod du postade skall vara? I MyApp extends Application?
     
    Last edited: 10 jul 2011
  9. nadam

    nadam Youth Droid Medlem

    Blev medlem:
    10 feb 2010
    Inlägg:
    182
    Mottagna gillanden:
    31
    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact

    MINA ENHETER

    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact
    Ja, det är en smaksak. För det mesta så fungerar båda varianterna lika bra. Jag använder själv Application-klass ibland.

    Nej, koden jag postade var tänkt att vara i själva DatabaseHelper-klassen. Då slipper man beroendet mellan MyApp och DatabaseHelper.
     
  10. Adam2

    Adam2 Adult Droid Medlem

    Blev medlem:
    26 jul 2010
    Inlägg:
    732
    Mottagna gillanden:
    55

    MINA ENHETER

    Lagt in getInstance() nu men har ett litet problem. Försöker i min MainActivity onCreate() göra följande

    Kod:
    dbh = DatabaseHelper.getInstance(MyApp.getContext());
    men får NullPointerException. Däremot fungerar följande bra

    Kod:
    dbh = DatabaseHelper.getInstance(getApplicationContext());
    MyApp ser ut såhär:

    Kod:
    public class MyApp extends android.app.Application {
    
        private static MyApp instance;
    
        public MyApp() {
            instance = this;
        }
    
        public static Context getContext() {
            return instance;
        }
    }
    
    Sen en annan fråga: I DatabaseHelper har jag en constructor som i dagsläget ser ut såhär:

    Kod:
    	public MyHelper (Context ctx){
    		this.context = ctx;
    		prefs = PreferenceManager.getDefaultSharedPreferences(context);
    	}
    Bör jag köra MyApp.getContext() istället för context eller ska man göra på något annat vis? :ehm:

    EDIT: Kan man "tacka" redigeraknappen? :P Jag hade fuckat upp min manifest... Tror jag är på banan igen.
     
    Last edited: 10 jul 2011
  11. nadam

    nadam Youth Droid Medlem

    Blev medlem:
    10 feb 2010
    Inlägg:
    182
    Mottagna gillanden:
    31
    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact

    MINA ENHETER

    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact
    Konstruktorn ser bra ut, men bör göras private istället för public eftersom den bara ska anropas internt från getInstance().

    getInstance() anropar du från exempelvis MainActivity.onCreate() med följande:
    Kod:
    dbh = DatabaseHelper.getInstance(this);
    Någon kod i MyApp behövs inte när man använder detta sätt.
     
  12. Adam2

    Adam2 Adult Droid Medlem

    Blev medlem:
    26 jul 2010
    Inlägg:
    732
    Mottagna gillanden:
    55

    MINA ENHETER

    Självklart private.. Tack!

    Är det verkligen bra att använda this? Enligt ditt första svar gör ju detta att databaseHelper håller på en referens till MainActivity (och därför kan ju inte den GC:as). Eller gäller inte det längre när jag använder getInstance() :ehm:
    Sorry att jag inte fattar detta riktigt!

    Jag har skapat en MyApp extends Application enligt ovan och tänkte att det skulle vara bättre att använda MyApp.getContext() än this (enligt resonemang ovan). :S
     
  13. nadam

    nadam Youth Droid Medlem

    Blev medlem:
    10 feb 2010
    Inlägg:
    182
    Mottagna gillanden:
    31
    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact

    MINA ENHETER

    Operatör:
    Hallon
    Telefon:
    Sony Xperia XZ1 Compact
    Precis, getInstance() sparar inte en referens till MainActivity utan plockar istället ut en referens till ApplicationContext, så då kan MainActivity fortfarande GC:as vid behov.