Speicherverbrauch

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Speicherverbrauch

    Ich habe schon wieder ein merkwürdiges Problem.

    Ich habe zwei Activities mit jeweils einem einzigen einfachen Button.
    Bei Click auf den Button der Activity A soll Activity B per Intent gestartet werden und A soll sich beenden.

    Bei Klick auf den Button der von A gestarteten Activity B soll wieder A gestartet und B beendet werden.
    ( Also schalte ich mit den Buttons immer zwischen A und B hin und her.)

    Das Problem: Bei jedem Wechsel zwischen den Activities wird wieder neuer Speicher belegt, der Verbrauch steigt also dauernd an bis kein Speicher mehr zur Verfügung steht.

    Meine Frage : Wie bekommt man das richtig hin, dass eine beendet App ihren Speicher wieder freigibt.

    Hier der Code:

    Quellcode

    1. public class MainActivity extends AppCompatActivity
    2. {
    3. private Button buttonStart_B;
    4. @Override
    5. protected void onCreate(Bundle savedInstanceState)
    6. {
    7. super.onCreate(savedInstanceState);
    8. setContentView(R.layout.activity_main);
    9. buttonStart_B = findViewById ( R.id.buttonStartB );
    10. }
    11. public void onClick_StartB( View v )
    12. {
    13. Intent myIntent = new Intent ( this, ActivityB.class );
    14. this.startActivity ( myIntent );
    15. finish();
    16. return;
    17. }
    Alles anzeigen


    Quellcode

    1. public class ActivityB extends AppCompatActivity
    2. {
    3. private Button buttonStart_A;
    4. @Override
    5. protected void onCreate(Bundle savedInstanceState)
    6. {
    7. super.onCreate(savedInstanceState);
    8. setContentView(R.layout.activityB);
    9. buttonStartA = findViewById ( R.id.buttonSettings );
    10. }
    11. public void onClick_StartA( View v )
    12. {
    13. Intent myIntent = new Intent ( this, MainActivity.class );
    14. this.startActivity ( myIntent );
    15. finish();
    16. return;
    17. }
    Alles anzeigen
  • Hallo eigentlich sollte das der GC selbständig im Hintergrund erledigen.

    Wahrscheinlich schaltest du zu schnell hin und her und der GC ist nicht so schnell.

    Um die activity sicher zu beenden füge noch system.exit(0) nach dem finish hinzu.

    Somit sollte der GC den Speicher freigeben. Was aber immer etwas dauern kann. Denn auch das freigeben von Speicher kostet cpu Zeit und damit Akku.
    Und genau da setzen die moderneren System an und versuchen einzusparen.

    Einen Wechsel von einer activity zur anderen aller 2 sek macht der User eigendlich nicht.
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von jogimuc ()

  • So schnell wechsel ich auch nicht.
    Der angegebene Code war nur als vereinfachtes Beispiel gedacht, bei dem das Problem aber auch real passiert.

    Bei dem "richtigen" Projekt schalte ich etwa 5 Mal innerhalb von ca 20 Minuten hin und her und danach ist das System so blockiert, das noch ein Neustart hilft.

    Was ich eigentlich erreichen will ist folgendes:
    Ich habe eine Main-Activity mit einem Main Layout.
    Und ich habe 3 "Neben"-Activities mit ihrem eigenen Layout ( zum Beispiel einen Texteditor ).
    Wenn ich mal ein Textfile bearbeiten will, drücke ich auf einen Button der Main Activity, starte damit die Editor Activity
    und nach dem Abspeichern des Textes wieder zurück zur Main Activity.
    Und wenn ich das 3 Mal gemacht habe, ist das Handy aus Speichermangel nicht mehr bedienbar.

    Das liegt aber nicht direkt am Editor, denn das Gleiche passiert auch mit einer einfachen Einstellungs-Activity mit ein paar Edit Feldern und Buttons.

    Es wäre schön, wenn man einer einzigen Activity mehrere Layouts zuweisen und hin und herschalten könnte, aber genau davon raten viele Tutorials ab und empfehlen lieber mehrere Activities.

    Nur: Irgendwas muss ich dabei falsch machen.
  • Es wäre schön, wenn man einer einzigen Activity mehrere Layouts zuweisen und hin und herschalten könnte, aber genau davon raten viele Tutorials ab und empfehlen lieber mehrere Activities.
    Das layout tauschen ist wirklich keine gute Idee und auch Fehler anfällig. Das würde ich Fragmente benutzen.


    Nur: Irgendwas muss ich dabei falsch machen.

    Ja bestimmt nur kann ich da so aus der fernen nichts dazu sagen.

    Bestimmt benutzt du Speicher den du nicht wieder zurück gibst.
    Beendest schließt vielleicht offne Dateien nicht, schließt auch keine Streams ....
    Vielleicht machst du auch viel mit static oder du benutzt sehr große Bilder Icons ….

    Ohne Code kann man da wenig sagen.


    Beende mal alle Instanzen und Objekte in der onDestry() Methode. Die wird beim zerstören der Activity aufgerufen.



    Wie viel RAM hat denn dein Handy?
    Welche Android Version?
    Ist dieses Verhalten auch bei anderen Apps oder nur bei deiner APP?

    PS auch ein Grund könnte sein das du threads benutzt und dise nicht beendet. Die dann so zusagen ewig laufen.
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von jogimuc ()

  • Nein, wie gesagt:
    Um alles auszuschließen habe ich eine neue App geschrieben, die aus zwei Activities besteht, und die im Layout jeweils nur einen einzigen Button haben, um zwischen den Activities umzuschalten.
    Der Code steht ja in meinem ersten Thread.

    Ich habe jetzt mal was anderes versucht:
    Eine einzige Activity und zwei Layouts.
    Beide Layouts haben jeweils einen einzigen Button.

    Und ich schalte jetzt nur die Layouts um.

    Also klickt man auf den Button im Main Layyout, wird im onClickListener nur die Funktion setContentView (zweitesLayout) aufgerufen. Und beim Button des zweiten Layouts wieder setContentView vom MainLayout. Und wieder wird bei jedem Wechsel fleißig Speicher allokiert und nicht freigegeben.

    Was soll denn da falsch sein???

    Wieviel Speicher das Handy hat, ist unerheblich. Denn egal, wieviel es hat, irgendwann ist Ende, wenn dauernd neuer Speicher belegt wird.
    Das hier dauernd Speicher allokiert wird, sehe ich im Emulator/Profiler.

    Der Effekt, das mein Handy unbedienbar wird, passiert natürlich bei der richtigen App, die sehr viel mehr Speicher allokiert, als diese kleinen Beispiele.Die richtige App wäre zu groß und unübersichtlich, um sie hier anzugeben.

    Aber das hier dauernd Speicher allokiert wird, passiert auch im Kleinen hier bei diesen kleinen Beispielen.

    Hier der neue Code, bei dem ebenfalls bei jedem Button-Klick immer wieder neuer Speicher belegt wird.:
    Ich habe hier zwar Unwichtiges weggelassen, aber die onClick Handler sind vollständig.

    Quellcode

    1. private Button buttonToMain;
    2. private Button buttonToSecond;
    3. onCreate()
    4. {
    5. // Hauptfenster anzeigen
    6. setContentView ( R.layout.activity_main );
    7. // der Button zum Anzeigen des zweiten Layouts
    8. buttonToSecond = findViewById ( R.id.button_second );
    9. }
    10. // onClick-Handler für den Button Second layout
    11. public void onClick_Second( View v )
    12. {
    13. // Zweites Layout anzeigen
    14. setContentView ( R.layout.second );
    15. // Button zum Zurueckschalten zu main
    16. buttonToMain = findViewById(R.id.button_main);
    17. return;
    18. }
    19. // onClick-Handler für den Button zurueck zum main Layout
    20. public void onClick_Main( View v )
    21. {
    22. // Main Layout anzeigen
    23. setContentView ( R.layout.main );
    24. return;
    25. }
    Alles anzeigen
  • Hallo das es mit den ständigen ContentView nicht geht hätte ich dir auch sagen können war mir auch klar. Hatte gestern keine Zeit mehr.

    Wenn du dir mal klar machst was in der Methode alles gemacht wird, ist das auch logisch. Es wird immer wieder dein Layout eingelesen und die Views Objekte instanziiert. Also das layout immer wider neu aufgebaut. Beim beenden der Activity werden die Instanzen ungültig und der GC kann sie freigeben.
    Da du die activity nicht beendest werden ständig von setcontentview erstellten Objekte nicht zurückgeben.

    Wenn du ständig das Layout ändern willst solltest du das selber machen, und nicht setcontentview überlassen. Mit Hilfe des Inflater und natürlich die benutzen Instanzen auch wieder freigeben.
    Das ist auch der Grund warum das ändern des Layouts in der Activity so Fehler anfällig ist und davon angeraten wird. Auch ich habe dir abgeraten, wenn du so etwas willst braucht sind Fragmente die besser Wahl. Dafür wurden sie auch geschaffen.


    Im übrigen ist es mir noch nicht passiert das so eine einfache App wie in deinem ersten Post das Handy lahm legt.

    Auf die Punkte die ich angesprochen habe geht du gar nicht ein.sondern suchst den Fehler etwas an der falschen Stelle.
    Die Möglichkeit das der Speicher nicht freigeben werden kann weil Objekte Instanzen nicht ungültig werden. Aber trotzdem neue Objekte herstellt werden gehst du auch nicht ein.



    Zu diesen beiden Klasse im Post 1
    Frage ich mich warum du in der Activity A den Button der Activty B suchst.
    Benutzen tust du ihn nicht.

    Den brauchst du gar nicht , den du benutzt bestimmt das OnClick im XML um einen Bezug zum Clicklistner zu haben.
    In der anderen Klasse Activty B suchst du den Button A .

    Das mit den findviewbyi und anschließenden setzen eines klicklistner braucht man bei Button wo das onClick im XML nicht benutzt wird.
    Ich persönlich Finde das sowieso überholt den bei Fragmente geht das auch nicht mehr.

    Warnlicht ist der GC zu langsam um die Instanz des Button oder sogar der activity löschen und du erstellst somit auch wieder eine neue Instant des Button.

    Eigentlich solltest du da einen Nullpointer bekommen.
    Merkst das aber nicht da du die variable gar nicht benutzt.
    Wenn du es mir nicht glaubst teste es und setze den Text des Buttons.

    Da ich deine Layouts nicht kenne gehe ich mal davon aus das der Button buttonStart_B in dem Layout activityB ist.
    Wenn dem so ist was willst du damit in der activity A?


    Lasse das FindVievById in den Klasse weg und schaue mal ob dir die Handy noch abstürzt.
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von jogimuc ()

  • Noch ein Tipp denn ich auch schon erwähnt habe ist die Methode.
    onDestry die beim beenden der activity aufgerufen wird. Setze doch da mal ein Log und schaue in der logcat ob sie aufgerufen wird. Wenn ja wird sie beendet. Ob dabei alle Instanzen bei deiner richtigen App gelöscht werden ist damit aber noch nicht bewiesen.
    Nur das wirklich die activity beendet ist und sie beim Aufruf wieder erstellt wird. Was der Standart ist bein wegsel der activity mit finish.


    developer.android.com/guide/co…vities/activity-lifecycle
    Ein Feedback auf Tipps ist auch schön. :P
  • Das mit dem Lahmlegen des Handys passiert nur bei meiner eigentlichen App, die aus einigen tausend Zeilen Code besteht und darum zu groß und unübersichtlich ist, um sie hier darzustellen.

    Darum habe ich nochmal eine Mini-App geschrieben, die nur dazu da ist, das Problem zu demonstrieren.

    Die kleine , hier angegebene Demo belegt nur sehr wenig Speicher und das legt mein Handy natürlich nicht lahm, bzw. würde das erst nach tausenden Schaltversuchen machen. Man sieht bei dieser kleinen Demo aber deutlich im Emulator/Profiler, das sie Speicher belegt und nicht freigibt.

    Nur die eigentliche App, die ich hier der Größe wegen NICHT angegeben habe, belegt wesentlich mehr Speicher, und da sind unsere Handys bereits nach 5 Mal hin und herschalten nicht mehr bedienbar.

    Ich komme eigentlich aus der C-Ecke und bin es da gewohnt, das man das, was man reserviert, auch selbst wieder freigibt. Und es wäre schön gewesen, wenns bei Android auch so eine Möglichkeit gegeben hätte.

    Aber nochmal zurück zu meinem eigentlichen Problem:
    Das ist so: Ich möchte irgendwas erreichen, lese mich durch ziemlich viele unverständliche Tutorials und Dokus und entscheide mich dann für eine Methode, die mir halbwegs sinnvoll erscheint.
    Nachdem ich monatelang an der App programmiert habe, laufe ich irgendwann in eine Sackgasse, wo mir dann jemand sagt : "Das kannste so nicht machen, das musst du ganz anders, nämlich so machen".

    Und dann fange ich wieder von vorne an, programmiere wieder Monate und laufe in die nächste Sackgasse, wo mir wieder jemand Anderes sagt, das geht so auch nicht, du musst das hier nehmen.

    Genau so ist es mir jetzt wieder ergangen.
    Mir hat jemand empfohlen, den LayoutSwitcher zu verwenden. Ich hab mich da eingelesen, und siehe da: Mein Speicherproblem ist weg.

    Jetzt habe ich alles umgestellt und stelle fest: Jetzt geht das mit dem Back-Button nicht mehr, weil ich jetzt nicht mehr mehrere, sondern nur noch eine Activity habe.
    Ich dachte: Kein Problem, fängst du einfach das onBackPressed ab ..... Pustekuchen. Geht bei Activities nicht ( keine Ahnung, warum nicht ).

    Also werde ich wohl jetzt deinen Rat befolgen und mich in das Thema Fragmente einlesen, alles umschreiben um dann am Ende wieder festzustellen, das Fragmente leider keine Buttons unterstützen oder nur in chinesischer Sprache funktioniern..
  • Hallo

    bzw. würde das erst nach tausenden Schaltversuchen machen. Man sieht bei dieser kleinen Demo aber deutlich im Emulator/Profiler, das sie Speicher belegt und nicht freigibt.

    Das ich kann so nicht Bestätigen. Ich habe auch mal so weine einfache App mit zwei Activity und Button gemacht und im Profiler den Ram verbrauch beobachtet. Am Anfang nach zwei drei Wechsel ging er leicht hoch pegelt sich aber ein, und von da ab wird Speicher verbraucht und wieder freigegeben. Klar das freigeben ist etwas verzögert. Ist aber normal.
    Wenn das bei dir nicht so ist, stimmt irgendwas mit deinen Handy oder System nicht.
    Es kann nicht sein das sich der Speicher bei so einer einfachen App ständig zu nimmt und nicht wieder freigegen wird.


    Ich komme eigentlich aus der C-Ecke und bin es da gewohnt, das man das, was man reserviert, auch selbst wieder freigibt. Und es wäre schön gewesen, wenns bei Android auch so eine Möglichkeit gegeben hätte.


    Ist es ja auch in einem gewissen Rahmen. Du kannst es zwar nicht selber machen wie in C aber du kannst dem GC sagen das du das Objekt die Instanz nicht mehr brauchst und er es Freigeben kann.

    Lösche einfach die Instanz wenn du sie nicht mehr brauchst.
    „Null“ bei Java ist gleich wie bei C.


    Wenn du also eine Variable auf null setzt wird sie vom GC erkannt und der reservierte Speicher wird freigegeben. Natürlich nicht gleich wie bei C, sondern wenn der GC Zeit hat oder wen der Speicher knapp wird sonnst tut er nichts.


    Also wie ich schon sagte solltest du in der onDestry() Methode die ja beim beenden durchlaufen wird die wichtigsten Variablen wieder auf „null“ setzen damit der GC weiß das sie nicht mehr gebraucht werden.


    Auch sagte ich dir das du nach dem "finish()" auch ein "System.exit()" machen sollst. Denn damit sollte der GS nach nicht benutzten Instanzen suchen und die Activity wirklich richtig beenden.
    Das kannst du auch im Emulator beobachten. Das nach einem System.exit(0) die Activity komplett neu aufgebaut wird. Der Wechsel von der einen Activity zu anderen dauert dann etwas länger. Auch werden damit noch eventuell laufende Threads beendet.

    finish();
    System.exit(0);

    Quellcode

    1. // benutzen Speicher wieder freigeben beim Beenden.
    2. List<String> farben;
    3. protected void onCreate(Bundle savedInstanceState) {
    4. ....
    5. farben = new ArrayList<String>();
    6. farben.add("rot");
    7. farben.add("blau");
    8. }
    9. protected void onDestroy() {
    10. super.onDestroy();
    11. farben=null;
    12. }
    Alles anzeigen
    Also werde ich wohl jetzt deinen Rat befolgen und mich in das Thema Fragmente einlesen, alles umschreiben um dann am Ende wieder festzustellen, das Fragmente leider keine Buttons unterstützen oder nur in chinesischer Sprache funktioniern..

    Ob dir das hilft mag ich zu bezweifeln, wenn du deine Speicherverwaltung nicht in den Griff bekommst.
    Und solche Sachen wie Button nur in Chinesisch zeigt mir das du immer noch an den Falschen stellen suchst.




    Ich dachte: Kein Problem, fängst du einfach das onBackPressed ab ..... Pustekuchen. Geht bei Activities nicht ( keine Ahnung, warum nicht ).
    Stimmt auch nicht.


    In eine Activity läst sich der OnBack Button abfangen. Wie hast du das den bis jetzt gemacht?

    In eine Activity gibt es weiterhin die Methode „onBackPressed“
    developer.android.com/referenc…tivity.html#onBackPressed()

    Das hat mit der AppBar nichts zu tun.
    du willst doch die Hardware ( neu Software) Taste benutzen oder?



    Besonders bei deinem Versuch mit dem LayoutSwitcher musst du extrem darauf aufpassen wo du dich gerade befindest, um auch richtig und sinnvoll zu Reagieren.
    Damit meine ich , du musst immer wissen welches Layout gerade activ ist und welche IDs (Button,Texte …) gerade im Layout vorhanden (geladen) sind. Notfalls musst du auch ständig immer wieder das findviewbyId machen, und prüfen ob eventuell Nullpointer entstanden sind. Das ist meiner Meinung sehr aufwendig und Fehler trächtig.


    Diesen Vorschlag wird dir bestimmt kein erfahrener Programmierer geben.




    Zu der Frage viel RAM das Handy hat. Gab es keine Antwort. Ich finde das schon etwas wichtig. Denn die maximale Standart Heap Größe ist vom verbauten RAM abhängig. 1 GB RAM ca 128 MB Heap
    Bei 2gb schon 256 MB Heap als Standart.
    Und wenn die erreicht ist sollte der GC spätestens aktiv werden.
    Und jenachdem was du machst also wofür du Speicher verbrauchst. Ist das etwas entscheidet. Bei vielen großen Bildern zb. Grossen Speicher intensiven Listen....

    Welche Meldung bekommst du denn überhaupt wenn das Handy hängt?
    Wäre auch nicht schlecht zu wissen.

    Benutzt du etwa auch thread die nicht richtig beendet werden. Denn wenn du keine Meldung über Out of Memory bekommst. Scheint mir das mehr in diese Richtung zu gehen.




    Das liegt aber nicht direkt am Editor, denn das Gleiche passiert auch mit einer einfachen Einstellungs-Activity mit ein paar Edit Feldern und Buttons

    Wenn dem wirklich so ist würde mich diese activty mal interessieren
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von jogimuc ()

  • Also erstmal vielen Dank, was ihr euch ( speziell jgimuc .... du solltest dich in meister yoda umbenennen, klingt ähnlich, triffts aber besser ) für eine Mühe mit mir macht.

    Also, das mit dem Speicherproblem ist so:
    Ich hatte anfangs einfach zwischen den Layouts umgeschaltet.

    Dann habe ich gelesen, das das Mist ist, wenn man mehrere "Fenster" hat, solle man das mit einzelnen Activities machen.
    Die Activities selbst sind sehr umfangreich ( z.B. hat die Hauptaktivity alleine etwa 80 Image-Buttons ).
    Es werden Threads für eine TCP/IP Kommunikation gestartet, List Views, jede Menge Klassen usw.
    Und das braucht natürlich alles Speicher.
    Dann habe ich mit viel Aufwand alles umgestellt, und war zufrieden, bis ich gemerkt habe, das mein Handy nach ein paar Umschaltungen träge wurde.

    Du hast recht: Ich habe meine Speicherverwaltung nicht im Griff. Ist aber auch kompliziert.
    Bei C rufe ich eine delete-Funktion auf und das Objekt ist zerstört.

    Bei Android setze ich die Zeiger auf die Objekte ( wie du empfohlen hast ) auf null und ..... nix passiert, der Speicher ist immer noch weg.
    Ich hätte eigentlich auch zwei Dinge erwartet.

    Entweder, wenn die Activity beendet wird, ist auch der belegte Speicher freigegeben. Dann muss ich die Objekte beim Neustart wieder neu erzeugen.
    Oder der Speicher bleibt allokiert, dann muss es aber auch eine Möglichkeit geben, die alten Objekte wieder weiterzunutzen, wenn die Activity neu gestartet wird.
    Beides ist aber scheinbar nicht der Fall.

    Also wollte ich rauskriegen, wer da den Speicher verbraucht, habe eine neue Mini-App geschrieben und rausgefunden, das es schon bei den Steuerelementen mit findViewById passiert, aber bestimmt bleibt auch der andere Speicher blockiert.

    Ich habe eine generelle Antwort erwartet, wie man mit einem solchen Problem umgeht ( und sie auch bekommen, nur funktionierts scheinbar immer noch nicht richtig ).

    Mein aktueller Stand
    Im Internet hatte ich an anderer Stelle gelesen, man könne auch das mit den vielen Activities vergessen und, wenn man mehrere Fenster hat, den ViewFlipper verwenden. ( Oder, wie auch du sagtest, Fragmente ).

    Helfen würde beides schon, denn dann werden nicht dauernd neue Objekte angelegt, weil man ja immer innerhalb einer einzigen Activity bleibt.

    Aber, wie schon befürchtet, klappt es anfangs immer gut, nur stolpert man später wieder über das nächste Hindernis.
    Das erste ( das das mit dem Back-Button nicht geht ) habe ich gelöst.
    Die CompatActivity hat tatsächlich KEINE onBackPressed. Jedenfalls bekomme ich immer eine Fehlermeldung, das man eine Methode einer Klasse nur überschreiben kann, wenn die Klasse sie implementiert, und das deute ich mal als onBackPressed oder so gibts bei Activities nicht, nur bei FragmentActivities.
    ich hab's aber mit der onKeyDown Methode hinbekommen. Wenn man's weiß, ist es logisch. Man kommt nur nicht drauf ;)

    Jetzt habe ich das nächste Problem:
    Ich habe eine ListView mit einem CustomAdapter. Die Menge der Listeneinträge ist dynamisch. Das hat bisher gut funktioniert, weil zur Anzeige der Liste bisher eine Activity gestartet wurde, die die Liste und den Adapter erzeugt und dann anzeigt.
    Ich muss jetzt eine Möglichkeit finden, dem Adapter eine neue Liste zuzuweisen, ohne immer einen neuen Adapter zu erzeugen., denn sonst habe ich wieder das Speicherproblem
  • Ich denke, es hat alles nichts damit zu tun, was du vermutest.

    Ich habe jetzt zum Beispiel nur noch eine einzige App.
    Hier passiert das Gleiche, und da diese App während des Tests niemals beendet wird und immer aktiv bleibt,
    kann es nichts mit Beenden von Apps zu tun haben.

    Auch die Frage nach der Größe ist unerheblich.
    Wenn es ein einziges Byte wäre, dann kann ich bei 1GB Speicher 1 Million mal umschalten, dann knallt's
    Wenn es um 500MB geht, knallt es beim ersten Umschalten.
    Das ist der einzige Unterschied.
    Aber das Problem, das dauernd neuer Speicher angefordert wird bleibt bestehen.

    Aber ich bin schon einen Schritt weiter, obwohl ich mir's immer noch nicht erklären kann.

    Folgende Bedingungen:
    Eine einzige Activity mit einem Main Display.
    (Da sind die ca. 80 Buttons drauf, einer davon schaltet per ViewFlipper auf ein anderes Sekundär-Display um.
    Per Back-Button schalte ich vom Sekundär Display auf das Main Display zurück.

    Das andere Display beinhaltet nur einen ListView, der mit einem Custom Adapter verbunden ist.

    Das Sekundäre Display verwende ich für zwei Fälle:

    1. Es soll eine Liste mit insgesamt 54 möglichen Aufgaben-Titeln anzeigen.
    ( Also steht dann da in der Listview untereinander Aufgabe1, Aufgabe2 ..... Aufgabe54 )
    Da die Liste für diesen Fall konstant ist, habe ich diese Aufgaben-Titel als static final string [] taskTitles = { "Aufgabe1","Aufgabe2" ....... }; angelegt.

    2. Es soll eine Liste mit den Aufgaben anzeigen, die jemandem zugeordnet wurden.
    ( Also z.B. jemand hat 3 Aufgaben bekommen, und zwar Aufgabe 5, Aufgabe 14 und Aufgabe 45 )
    Hier habe ich eine globale Variable String [] taskTitles; angelegt

    Wenn im Hauptfenster der Button "Alle möglichen Aufgaben" angeklickt wird, weise ich dem CustomAdapter die statische Liste zu:
    // Statische Aufgaben-Liste zuweisen (54 Stk.) customAdapter.taskTitles = taskTitles;// Sekundärdisplay anzeigen viewFlipper.setDisplayedChild( DISPLAY_TASKLIST );

    Wenn im Hauptfenster der Button "Zugewiesene Aufgaben" angeklickt wird, mache ich das Gleiche, nur diesmal erzeuge ich das Array mit den Aufgabentiteln dynamisch:

    // Dynamische Liste mit zugewiesenen Aufgaben erzeugen // (numberOfAssignedTasks ist irgendwas zwischen 1 und 16) taskTitlesAssigned = new String [ numberOfAssignedTasks ];// Dynamische Liste zuweisen customAdapter.taskTitles = taskTitlesAssigned;// Sekundärdisplay anzeigen viewFlipper.setDisplayedChild( DISPLAY_TASKLIST );In der onKeyDown reagiere ich auf den Back Button und schalte auf das Main Display zurück:public boolean onKeyDown(int keyCode, KeyEven
    {
    if ( keyCode == KeyEvent.KEYCODE_BACK )
    {
    switch(display) // Welches Display ist gerade angezeigt?
    {
    case DISPLAY_TASKLIST: // Das Sekundärdisplay ist aktiv
    display = DISPLAY_MAIN; // Auf Hauptdisplay zurück
    break;// Hier kommen noch weiter, sind aber uninteressant, passier genau das Gleiche
    default:
    return super.onKeyDown(keyCode, event);
    }
    viewFlipper.setDisplayedChild(display); // Auf das gewünschte Display zurückschalten// Und hier gebe ich die dynamisch allokierte Liste wieder frei........ ( Ist das so richtig ?)
    taskTitlesAssigned = null;

    System.gc();

    return true;
    }

    return super.onKeyDown(keyCode, event);}Und jetzt das Merkwürdige:Das Hin-Und Herschalten mit der dynamischen Liste funktioniert ohne wachsenden Speicher.Zeige ich aber die statische Liste im Sekundärfenster an, wird beim Aktivieren des Sekundärfensters Speicher angefordert, Beim Rückschalten auf das Hauptfenster wieder.Und wenn ich wieder auf Sekundär schalte wieder.
  • Hallo
    Erstmal eins zum Verständnis das was du als Liste bezeichnest ist keine dynamische Liste sondern ein Array. Was durch das new String[] ihre Grösse bekommt.
    Danach auch nicht mehr änderbar ist.
    Variablen mit [] sind statische Arrays. Keine Listen die dynamisch erweiterbar sind.


    Eine List ist so etwas hier
    List<String> mList = new ArrayList<String>();
    Zugrif mit get(), set(), add()


    In deinem Adapter wird bestimmt das Array in eine Liste übertragen denn der Adapter arbeitet mit Linsten nicht mit Arrays.
    developer.android.com/reference/android/widget/ArrayAdapter


    Eine Liste (dynamisch) hat immer Spitze Kammern <>




    Hier habe ich eine globale Variable String [] taskTitles; angelegt
    meiner Meinung nach gibst du mit "taskTitlesAssigned = null;" das Statische Array nicht frei.

    ein im Quellcode erstelltes Member Klassen Array wird bestimmt nicht freigegeben.
    Außer du erstellst sie in einem Block ( Methode) da könnte das schon gehen.
    Mit erstellen meine ich nicht zuweisen sonder Definieren.
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von jogimuc ()

  • Hier mal ein einfache Beispiel wo du beobachten kannst wie sich der GC verhält.
    Es macht auch sinn es mal unter verschiedenen Android Versionen laufen zu lassen. Denn das verhalten des GC ist da sehr unterschiedlich.
    Beim Speichern werden ca. 4 MB auf dem Heap gespeichert. Was nach einer bestimmten Menge ohne löschen zu einen OutOfMemory füht.
    Bei Android 8 frirt die App erstmal ein der GC versucht ohne ein zutuen des Users den Speicher zu bereinigen schafft es nicht bricht dann aber doch ab.
    Bei Android 5 wird zB. gleich beendet.
    Du sieht das also auch die Android Version eine Rolle spielt.

    Java-Quellcode

    1. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    2. private String[][] as;
    3. private int index = 0;
    4. private Runtime runtime = Runtime.getRuntime();
    5. private TextView tv;
    6. @Override
    7. protected void onCreate(Bundle savedInstanceState) {
    8. super.onCreate(savedInstanceState);
    9. setContentView(R.layout.activity_main);
    10. tv = findViewById(R.id.tv);
    11. findViewById(R.id.alloc).setOnClickListener(this);
    12. findViewById(R.id.showString).setOnClickListener(this);
    13. findViewById(R.id.loeschen).setOnClickListener(this);
    14. findViewById(R.id.loeschenGC).setOnClickListener(this);
    15. findViewById(R.id.loeschenRGC).setOnClickListener(this);
    16. as = new String[1000][];
    17. showMemory();
    18. }
    19. @Override
    20. public void onClick(View v) {
    21. switch (v.getId()){
    22. case R.id.alloc:
    23. if (as == null) {
    24. as = as = new String[1000][];
    25. index=0;
    26. }
    27. String[] star = new String[100_000];
    28. for (int i = 0; i < 100_000; i++)
    29. star[i] = new String(" ");
    30. as[index++] = star;
    31. showMemory();
    32. break;
    33. case R.id.showString:
    34. showMemory();
    35. break;
    36. case R.id.loeschen:
    37. as = null;
    38. showMemory();
    39. break;
    40. case R.id.loeschenGC:
    41. as = null;
    42. System.gc();
    43. showMemory();
    44. break;
    45. case R.id.loeschenRGC:
    46. as = null;
    47. runtime.gc();
    48. showMemory();
    49. break;
    50. }
    51. }
    52. private void showMemory() {
    53. long max = runtime.maxMemory()/1024;
    54. long total = runtime.totalMemory()/1024;
    55. long free = runtime.freeMemory()/1024;
    56. long consumed = (runtime.totalMemory() - runtime.freeMemory())/1024;
    57. tv.setText("\n\nMax Memory (KB): " + Long.toString(max) +
    58. "\nTotal Memory (KB): " + Long.toString(total) +
    59. "\nFree Memory (KB): " + Long.toString(free) +
    60. "\n\nMemory consumed (KB): " + Long.toString(consumed)+
    61. "\n\n Index: " + Long.toString(index)
    62. );
    63. }
    64. }
    Alles anzeigen


    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:tools="http://schemas.android.com/tools"
    4. android:layout_width="match_parent"
    5. android:layout_height="match_parent"
    6. tools:context=".MainActivity"
    7. android:orientation="vertical" >
    8. <Button
    9. android:id="@+id/alloc"
    10. android:layout_width="wrap_content"
    11. android:layout_height="wrap_content"
    12. android:text="Alloc Speicher" />
    13. <Button
    14. android:id="@+id/showString"
    15. android:layout_width="wrap_content"
    16. android:layout_height="wrap_content"
    17. android:text="Zeige Speicher" />
    18. <Button
    19. android:id="@+id/loeschen"
    20. android:layout_width="wrap_content"
    21. android:layout_height="wrap_content"
    22. android:text="Speicher Löschen" />
    23. <Button
    24. android:id="@+id/loeschenGC"
    25. android:layout_width="wrap_content"
    26. android:layout_height="wrap_content"
    27. android:text="Speicher Löschen mit System GC" />
    28. <Button
    29. android:id="@+id/loeschenRGC"
    30. android:layout_width="wrap_content"
    31. android:layout_height="wrap_content"
    32. android:text="Speicher Löschen mit Runtime GC" />
    33. <TextView
    34. android:id="@+id/tv"
    35. android:layout_width="wrap_content"
    36. android:layout_height="wrap_content"
    37. android:textAppearance="?android:attr/textAppearanceSmall"
    38. android:text="Total Memory: "
    39. android:layout_marginTop="40dp" />
    40. </LinearLayout>
    Alles anzeigen
    Ein Feedback auf Tipps ist auch schön. :P
  • Na ja, das wäre ein Bisschen zu einfach zu sagen : Java ist die Sprache der Wahl, aber benutzen sollte man sie nicht.
    Strings sollten immer funktionieren, egal ob groß oder klein.
    Und wenn nicht, dann hat man was bei der Umsetzung der Programmiersprache falsch gemacht und nicht bei der Benutzung dwr Sprache.

    Das mit dem Static final habe ich nur erwähnt, weil da offensichtlich was nicht zusammenpasst:
    Lege ich die Arrays dynamisch an, tritt das Problem nicht oder weniger stark auf.
    Verwende ich ein statisches Array, dann wird dauernd Speicher angefordert. und nicht freigegeben.
    Eigentlich sollte man es eher umgekehrt erwarten, oder?

    Neue Erkenntnis: OK, das mit dem nicht zusammen passen ist geklärt. Wenn ich ein dynamisches Array verwende, enthält das deutlich weniger Elemente als das Statische. Der Effekt erklärt sich also dadurch, das der zusätzlich angeforderte und nicht freigegebene Speicher von der Größe des Arrays abhängt, welches die Listenelemente definiert. Der Mehrverbrauch fällt also bei kleinen Arrays nicht so sehr auf, ist aber trotzdem da.

    Also: Es hat definitiv nichts mit von mir allokiertem Speicher zu tun, sondern ist ein internes Problem

    Ich habe es jetzt mal weiter reduziert.
    Ich allokiere jetzt GAR nichts mehr ( kein new usw ).
    Ich schalte jetzt nur noch mit ViewFlipper zwischen einem Haupt-Display und einem Sekundär-Display mit einem ListView hin und her,
    Die Listitems hole ich aus einerm Array aus der CustomAdapter Klassse, das Array wird nicht verändert:
    private String [] taskTitlesAll = {
    "Titel1",
    "Titel2", u.s.w.

    "Titel52",
    "Titel53"
    };



    Quellcode

    1. // So schalte ich auf das sekundäre ListView-Display
    2. public void onClick_TaskList ( View v )
    3. {
    4. // TaskList-fenster anzeigen
    5. display = DISPLAY_TASKLIST;
    6. viewFlipper.setDisplayedChild( DISPLAY_TASKLIST );
    7. return;
    8. }
    9. // und so wieder zurück auf das Hauptdisplay
    10. public boolean onKeyDown(int keyCode, KeyEvent event)
    11. {
    12. if (keyCode == KeyEvent.KEYCODE_BACK)
    13. {
    14. switch (display)
    15. {
    16. case DISPLAY_TASKLIST:
    17. display = DISPLAY_MAIN;
    18. break;
    19. default:
    20. return super.onKeyDown(keyCode, event);
    21. }
    22. viewFlipper.setDisplayedChild(display);
    23. return true;
    24. }
    Alles anzeigen


    und der Speicherverbrauch steigt und steigt bei jedem Umschalten.

    Es tut mir Leid, aber für mich sieht das nach einem Android Bug aus.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Adreoid ()

  • und der Speicherverbrauch steigt und steigt bei jedem Umschalten.
    Es tut mir Leid, aber für mich sieht das nach einem Android Bug aus.

    Nein ein Bug ist das mit Sicherheit nicht.

    Du solltest dir mal überlegen was alles bei dem Umschalten gemacht wird. Es werden Methoden und Kassen aufgerufen die du gar nicht Siehst. Das System braucht sozusagen auch Speicher zum Umschalten .

    Das ist selbst in meinen Beispiel so wenn du öfters nur auf anzeigen Klickst.


    Wie Ich schon mehrfach sagte macht der GC nicht immer gleich den Speicher frei sondern erst wenn er knapp wird .
    Wie das der GC und wann der das macht ist auch Android Version und Geräte abhängig.


    Außerdem solltest du wissen das wir in Java „Call by Value“ haben und nicht „Call by Referenz“ wie in C.
    Was bei einem Methoden Aufruf meist etwas mehr Speicher verbracht als bei C.

    Da Mobile Geräte nicht so viel Speicher haben als Desktop Rechner. Ist es üblich geworden nicht mit so viel Statischen Sachen zu arbeiten, sondern mehr mit dynamischen Ellenenten.
    Das entspricht auch mehr den OOP Richtlinien.

    Wie hoch ist denn eigentlich der speicherverbrach vor und nach dem um schalten gib da mal genaue Zahlen. Oder besser zu wieviel Prozent ist der Heap vor dem umschalten gefüllt.
    Ein Feedback auf Tipps ist auch schön. :P

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von jogimuc ()