OutOfMemoryError beim ändern der ImageView Ressourcen

  • Antworten:21
Robbiani Renato
  • Forum-Beiträge: 602

28.12.2018, 21:34:45 via Website

Hallo zusammen

Ich erstelle ein Spiel worin man Bilder anklicken muss und dann verschwinden sie wieder. Diese Bilder erstelle ich mit "ImageView". Wenn ich das Spiel spiele, dann erscheint nach einem Moment ein "OutOfMemory". Nun frage ich mich ob ich die Views falsch anlege oder nicht korrekt entferne. Oder gibt es eine Möglichkeit um den Speicher auf zu räumen?

Nun die Angaben zu meinem Programm:

Die Fehlermeldung:

12-28 20:58:07.549 29054-29054/ch.robbisoft.mueckenfang E/Mückenfangen: Bild laden Posotion : -1 / 1

12-28 20:58:07.559 307-29494/? E/AudioSink: received unknown event type: 1 inside CallbackWrapper !
12-28 20:58:07.669 29054-29054/ch.robbisoft.mueckenfang E/art: Throwing OutOfMemoryError "Failed to allocate a 31674396 byte allocation with 16777120 free bytes and 27MB until OOM"
12-28 20:58:07.679 29054-29054/ch.robbisoft.mueckenfang E/AndroidRuntime: FATAL EXCEPTION: main
Process: ch.robbisoft.mueckenfang, PID: 29054
java.lang.OutOfMemoryError: Failed to allocate a 31674396 byte allocation with 16777120 free bytes and 27MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:745)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:566)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1014)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:3730)
at android.content.res.Resources.loadDrawable(Resources.java:3603)
at android.content.res.Resources.getDrawable(Resources.java:1852)
at android.content.Context.getDrawable(Context.java:410)
at android.widget.ImageView.resolveUri(ImageView.java:752)
at android.widget.ImageView.setImageResource(ImageView.java:408)
at ch.robbisoft.mueckenfang.GameActivity.setzeBild(GameActivity.java:190)
at ch.robbisoft.mueckenfang.GameActivity.eineMueckeAnzeigen(GameActivity.java:168)
at ch.robbisoft.mueckenfang.GameActivity.zeitHerunterzaehlen(GameActivity.java:133)
at ch.robbisoft.mueckenfang.GameActivity.run(GameActivity.java:251)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5938)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
12-28 20:58:07.709 838-29962/? E/android.os.Debug: ro.product_ship = true
12-28 20:58:07.709 838-29962/? E/android.os.Debug: ro.debug_level = 0x4f4c
12-28 20:58:07.869 7663-29973/? E/SQLiteLog: (284) automatic index on crash_info_summary(package_name_touched)
12-28 20:58:07.919 5787-5787/? E/SecureStorage: [ERROR]:SPID(0x00000000)Data block not found: -1
12-28 20:58:08.259 5787-5787/? E/SecureStorage: [ERROR]:SPID(0x00000000)Error processing data
12-28 20:58:08.259 5787-5787/? E/SecureStorage: [ERROR]:SPID(0x00000000)Android: read data error: -1
12-28 20:58:08.849 300-300/? E/SMD: DCD OFF

Der Code:

    private void setzeBild(ImageView biene, int vx, int vy) {
    Log.e("Mückenfangen","Bild laden Posotion : " + vx + " / " + vy);
    biene.setImageResource(MUECKEN_BILDER[vy+1][vx+1]);
}

   private void eineMueckeAnzeigen(){
    int breite = spielbereich.getWidth();
    int hoehe = spielbereich.getHeight();
    int muecke_breite = Math.round(massstab * 42);
    int muecke_hoehe = Math.round(massstab * 50);
    int links = zufallsgenerator.nextInt(breite - muecke_breite);
    int oben = zufallsgenerator.nextInt(hoehe - muecke_hoehe);

    ImageView muecke  = new ImageView(this);

// muecke.setImageResource(R.drawable.biene);
muecke.setOnClickListener(this);

    int vx;
    int vy;
    double faktor = 1.0;
    //Falls die Geschwindikeit 0 ist
    do{
        vx = zufallsgenerator.nextInt(3) - 1;
        vy = zufallsgenerator.nextInt(3) - 1;
    }while(vx==0 && vy==0);
    setzeBild(muecke, vx, vy);
    if(vx != 0 && vy != 0){
        faktor = 0.70710678;
    }
    vx = (int) Math.round(massstab * vx * faktor);
    vy = (int) Math.round(massstab * vy * faktor);

    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(muecke_breite, muecke_hoehe);
    params.leftMargin = links;
    params.topMargin = oben;
    params.gravity = Gravity.TOP + Gravity.LEFT;//links oben 51

    spielbereich.addView(muecke, params);
    muecke.setTag(R.id.geburtsdarum, new Date());
    muecke.setTag(R.id.vx, new Integer(vx));
    muecke.setTag(R.id.vy, new Integer(vy));
    mp.seekTo(0);
    mp.start();
}

Was kann ich machen, dass das Programm nicht mehr abstürzt?

Gruss Renato

— geändert am 28.12.2018, 22:00:42 durch Moderator

Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

28.12.2018, 21:42:28 via Website

Hallo Renato,

der Fehler leigt in der setztBild Methode.
Da setzt du eine neue Ressource auf deine ImageView, gibt aber den Speicher des vorhergehenden Bildes nicht frei. In solchen Fällen blickt der GC nicht, dass die Ressource nicht mehr benötigt wird.
Das sollte helfen:
https://stackoverflow.com/a/18197453

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

28.12.2018, 22:23:14 via Website

Ciao Pascal

Herzlichen Dank für die schnelle Hilfe. Aber das Spiel läuft nicht besser. Entweder mache ich was falsch oder dies ist nicht die Ursache.
Ich muss wohl noch weiter schauen.

Gruss Renato

Hilfreich?
Kommentieren
swa00
  • Forum-Beiträge: 3.704

28.12.2018, 22:37:28 via Website

Hallo Robbi,

wie Pascal schon erwähnt hat , benötigst du ein Speichgermanagement für deine Images.
So wie du das umgesetzt hast , wird es leider nicht funktionieren und irgendwann mal überlaufen.

Mit der folgenden Technik kannst du das umsetzen
https://developer.android.com/reference/android/util/LruCache

Allerdings würde ich Dir aus vereinfachten Gründen anraten , ggf eine Library zu verwenden , die
den Cache auch verwaltet
https://github.com/bumptech/glide

— geändert am 28.12.2018, 22:47:50

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

29.12.2018, 18:09:20 via Website

Hallo
Ich werde aus deiner setzeBild Methode nicht richtig schlau.

private void setzeBild(ImageView biene, int vx, int vy) {
    Log.e("Mückenfangen","Bild laden Posotion : " + vx + " / " + vy);
    biene.setImageResource(MUECKEN_BILDER[vy+1][vx+1]);
}

Was Ist „MUECKEN_BILDER[][]“ hast du in diesem Array für alle erdenklichen Bildschirm Positionen eine Ressourcen ID gespeichert?

Denn du willst ja dem ImageView ein Bild aus einer Ressource übergeben.

Eigentlich willst du doch nur die Position verändern und nicht das Bild selber.
Für mich macht die Methode keinen richtigen sinn.

Ansonsten glaube ich das du dich an diesem Beispiel Orientiert hast .
da macht der Autor es etwas anders.

https://github.com/dressler/android/blob/master/Mueckenfang/src/de/andriodnewcomer/mueckenfang/GameActivity.java

— geändert am 29.12.2018, 18:23:20

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

29.12.2018, 19:04:16 via Website

Ciao Jokel

In der Methode SetzeBild setze ich jeweils das Bild in welche Richtung die Mücke fliegt. Dies sieht wie folgt aus.

private static final int MUECKEN_BILDER[][]={
    {R.drawable.biene_nw, R.drawable.biene_n, R.drawable.biene_no},
    {R.drawable.biene_w, R.drawable.biene, R.drawable.biene_o},
    {R.drawable.biene_sw,R.drawable.biene_s, R.drawable.biene_so}
};

Aber mit deinem Link hast du Recht. Das ist das Beispiel. In diesem Fall mache ich was falsch. Ich werde nochmals alles anschauen. Herzlichen Dank für deine Hilfe.

Gruss Renato

Hilfreich?
Kommentieren
Gelöschter Account
  • Forum-Beiträge: 79

30.12.2018, 17:49:24 via Website

Hallo Renato,

in dem Beispielprojekt lädt die setzeBild-Funktion erstmal anhand des Resourcen-Namens die Resourcen-Id und setzt das dann in die ImageView ein. Weil du ja in deinem MUECKEN_BILDER Array schon die Resourcen-Ids stehen hast, müsste das soweit ich das verstehe eigentlich so richtig sein.
Was mir in dem Beispiel nicht so ganz klar ist, ist, wieso er das "getIdentifier" benutzt. Er müsste ja genau wie du die Resourcen IDs auch direkt zur Verfügung haben. (thinking)

Tritt der Fehler bei dir denn direkt beim starten auf, oder siehst du zumindest schon etwas auf dem Bildschirm, bevor der Fehler kommt? Ich hatte vor kurzem erst bei einer App auch so einen "OutOfMemoryError", der kam aber nicht sofort und ließ sich tatsächlich mit dem in der ersten Antwort beschriebenen Bitmap.recycle() beseitigen.

Jetzt fällt mir gerade noch ein: Wenn ich das Beispiel richtig verstehe, dann werden bei dem Spiel zufällig neue ImageViews generiert und dann die Resource je nach Richtung da rein geladen. Das würde ja bedeuten, dass jede neue Mücke auch neu das Bild lädt (glaube ich zumindest).

Vielleicht wäre es auch die Lösung, anstatt ein Array mit den ResourcenIds ein Array mit Bitmaps zu machen und die neun Mückenbilder beim Start der App einmalig da reinzuladen. Bei "setzeBild" würdest du dann statt "setImageResource" "setImageBitmap" benutzen. Weil die Bitmaps dann ja schon im Speicher vorliegen, werden sie nicht neu aus den Resourcen geladen und blockieren so auch bei mehrfacher benutzung nur ein Mal den Speicher (zumindest soweit ich weiß).
Wenn deine Mücken nicht gerade 4K-Auflösung haben sollte das ja vom Speicher her kein Problem sein. :-D

— geändert am 30.12.2018, 17:49:45

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

30.12.2018, 18:42:45 via Website

Ciao Robert

Herzlichen Dank für deine Antwort.
Der Fehler tritt nicht sofort auf. Ich kann eine weile Spielen und dann bleibt das Programm stehen. Ich werde mal versuchen die erste Lösung um zu setzten.

Das ist eine gute Idee von dir. Nein die Bilder sind lange nicht 4K. Es sind nur kleine Bildchen. Das könnte bestimmt weiter helfen.

Gruss Renato

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

30.12.2018, 19:57:39 via Website

Hallo ich würde sagen das die Instanz der ImageView die in „eineMueckeAnzeigen()“ erzeugt wird, nicht wieder freigegeben wird. Somit ewig im Speicher bleibt.
Bei der nechsten Mücke wird wider eine neue Instanz erstellt.
Wie schon gesagt aber nie gelöscht.

ImageView muecke = new ImageView(this);

In der Methode „mueckenVerschwinden()“ wird sie zwar vom Spielfeld entfernt, aber die Instaz wird nicht gelöscht, und somit auch nicht aus dem Speicher entfernt.

private void mueckenVerschwinden() {
        int nummer = 0;
        while(nummer < spielbereich.getChildCount()) {
            ImageView muecke = (ImageView) spielbereich.getChildAt(nummer);
            Date geburstdatum = (Date) muecke.getTag(R.id.geburtsdatum);
            long alter = (new Date()).getTime() - geburstdatum.getTime();
            if (alter > HOECHSTALTER_MS) {
                spielbereich.removeView(muecke);
                mp.pause();
            } else {
                nummer++;
            }
        }       
    }

du holst dir ja die Instanz mit

"ImageView muecke = (ImageView) spielbereich.getChildAt(nummer);"

somit kannst du sie auch mit "muecke= null;" wieder freigeben.
nachdem du sie vom Bildschirm entfernt hast.

if (alter > HOECHSTALTER_MS) {
                    spielbereich.removeView(muecke);
                                muecke = null;

An einen zu hohen Speicherverbrauch beim laden der Ressourcen, in der Methode „setzeBild“ glaube ich nicht.
Somit wird die sache mit dem Bitmaps auch nicht viel bringen.

Frage wie viele Runden oder wie viele Mücken werden den gleichzeitig angezeigt?

— geändert am 30.12.2018, 21:02:11

Hilfreich?
Kommentieren
Gelöschter Account
  • Forum-Beiträge: 79

30.12.2018, 21:14:41 via Website

Mal eine ganz andere Frage: Hat irgendjemand dieses Beispielprojekt schonmal laufen sehen? Das kommt mir nämlich irgendwie Alles ein bisschen komisch vor.

Funktioniert es erst seit du deine Änderungen gemacht hast nicht mehr?

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

31.12.2018, 08:09:59 via Website

Hallo Renato.
Ich gehe davon aus das du in deiner Version einiges geändert hast. Das zeigt mir auch schon deine eineMueckeAnzeigen() Methode. Im vergleich mit der von Github.

Auch Ist das Beispiel aus Github für API 8 geschrieben, ab API 21 gibt es das Interface „Camera.PreviewCallback“ nicht mehr wurde durch Camera2 ersetzt.

Das musst du also auch geändert haben damit das Spiel überhaupt läuft.

In dem Beispiel von Github ist es so das ständig neue Mücken erzeugt werden, auch wenn du keine Mücken fängst werden welche erstellt. Dies würde unweigerlich zum Speicher überlauf führen. Der Auto des Spiel gibt aber der Mücke ihr Geburtsdatum mit (muecke.setTag(R.id.geburtsdatum, new Date()))

In der „mueckenVerschwinden()“ Methode entfernt er zu alte Mücken die nicht gefangen wurden. Somit wird der Speicher etwas aufgeräumt. Wo bei das entfernen bei gefangenen Mücken wahrscheinlich nicht funktioniert, und sie wohl erst wenn sie zu alt sind entfernt werden. Eigentlich sollte es reichen nur die Mücke vom Bildschirm (Spielbereich ) zu entfernen. Um es dem GC etwas leichter zu machen sollte die Instanz auf „null“ gesetzt werden damit der GC es auch wirklich löscht.

So nun meine Frage wie machst du das mit dem löschen der Mücken? (Instanzen)

Ohne das du hier den Code Postest werden wir nicht helfen können.
Mutmaßungen helfen uns und dir nicht.

Auch mit dieser Instanz bin ich mir nicht sicher ob sie auch immer gelöscht wird.
„NV21Image nv21 = new NV21Image(bild, breite, hoehe);“
Aber das musst du sowieso geändert haben da es die Methode „onPreviewFrame“ ab API 21 nicht mehr gibt.
Denn die Methode „onPreviewFrame“ wird doch auch bei jedem SurfaceView also Updat der View durchlaufen oder täusche ich mich jetzt.

Wie gesagt ohne Code wird es keine Hilfe geben. Anhaltspunkte hast du nun genug.

Robert S.

Mal eine ganz andere Frage: Hat irgendjemand dieses Beispielprojekt schonmal laufen sehen? Das kommt mir nämlich irgendwie Alles ein bisschen komisch vor.

Auf API 8-21 und einem entsprechenden Handy könnte, sollte es laufen. Auf den heutigen API´s eher nicht warum habe ich schon gesagt. Einige Methoden sind veraltet.

LG und guten Rutsch.

— geändert am 31.12.2018, 09:20:37

Hilfreich?
Gelöschter AccountPascal P.
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

31.12.2018, 14:18:49 via Website

Ciao Jokel

Herzlichen Dank für deine Geduld. Das Beispiel stammt aus einem Buch welches in diesem Jahr erschienen ist. Ich habe das Programm mit allen Tipps angepasst. Ich kann jetzt zwar länger spielen aber der Überlauf geschieht immer noch.
Nun wie gewünscht der Code so wie ich es zur Zeit erstellt habe:

package ch.robbisoft.mueckenfang;

import android.app.Activity;
import android.app.Dialog;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.Date;
import java.util.Random;

public class GameActivity extends Activity implements View.OnClickListener,Runnable {

private static final long HOECHSTALTER_MS = 2000;
public static final int DELAY_MILLIS = 100;
public static final int SPIEL_ZEIT = 900;
public static final int SPEIL_FAKTOR = 300;
public static final float ANZAHL_BIENEN = 15f;
int punkte;
int runde;
boolean spiellaeuft;
int muecken;
int gefangeneMuecken;
int zeit;
private float massstab;
private Random zufallsgenerator = new Random();
private ViewGroup spielbereich;
private Handler handler = new Handler();
private MediaPlayer mp;

private static final int MUECKEN_BILDER[][]={
    {R.drawable.biene_nw, R.drawable.biene_n, R.drawable.biene_no},
    {R.drawable.biene_w, R.drawable.biene, R.drawable.biene_o},
    {R.drawable.biene_sw,R.drawable.biene_s, R.drawable.biene_so}
};

private class MueckeAnimaionListener implements Animation.AnimationListener {
    private View muecke;
    public MueckeAnimaionListener(View m){
        muecke = m;
    }
    @Override
    public void onAnimationEnd(Animation animation){
        handler.post(new java.lang.Runnable() {
            @Override
            public void run() {
                spielbereich.removeView(muecke);
                muecke.setAnimation(null);
                muecke = null;
            }
        });
    }
    @Override
    public void onAnimationRepeat(Animation animation){

    }
    @Override
    public void onAnimationStart(Animation animation){

    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.game);
    massstab = getResources().getDisplayMetrics().density;
    spielbereich = (ViewGroup) findViewById(R.id.spielbereich);
    mp = MediaPlayer.create(this, R.raw.summen);
    spielStarten();
}

private void spielStarten(){
    spiellaeuft = true;
    runde = 0;
    punkte = 0;
    starteRunde();
}

private void starteRunde(){
    runde = runde + 1;
    muecken = runde * 10;
    gefangeneMuecken = 0;
    zeit = SPIEL_ZEIT;
    bildschirmAktualisieren();
    handler.postDelayed(this, DELAY_MILLIS);
}

private void bildschirmAktualisieren(){
    TextView tvPunkte = (TextView)findViewById(R.id.points);
    tvPunkte.setText("Punkte : " + Integer.toString(punkte));
    TextView tvRunde = (TextView)findViewById(R.id.round);
    tvRunde.setText("Runde : " + Integer.toString(runde));

    TextView tvTreffer = (TextView)findViewById(R.id.hits);
    tvTreffer.setText(Integer.toString(gefangeneMuecken));
    TextView tvZeit = (TextView)findViewById(R.id.time);
    tvZeit.setText(Integer.toString(zeit/(1000/DELAY_MILLIS)));

    FrameLayout flTreffer = (FrameLayout)findViewById(R.id.bar_hits);
    FrameLayout flZeit = (FrameLayout)findViewById(R.id.bar_time);

    ViewGroup.LayoutParams lpTreffer = flTreffer.getLayoutParams();
    lpTreffer.width = Math.round( massstab * SPEIL_FAKTOR * Math.min(gefangeneMuecken, muecken) / muecken);

    ViewGroup.LayoutParams lpZeit = flZeit.getLayoutParams();
    lpZeit.width = Math.round(massstab * zeit * SPEIL_FAKTOR / SPIEL_ZEIT);
}

private void zeitHerunterzaehlen(){
    zeit--;

    if(zeit % (1000/DELAY_MILLIS) == 0) {
        float zufallszahl = zufallsgenerator.nextFloat();
        if (zufallszahl < muecken * ANZAHL_BIENEN / SPIEL_ZEIT) {
            eineMueckeAnzeigen();

            double wahrscheinlichkeit = muecken * ANZAHL_BIENEN / SPIEL_ZEIT;
            if (wahrscheinlichkeit > 1) {
                eineMueckeAnzeigen();
                if (zufallszahl < wahrscheinlichkeit - 1) {
                    eineMueckeAnzeigen();
                }
            } else {
                if (zufallszahl < wahrscheinlichkeit) {
                    eineMueckeAnzeigen();
                }
            }
        }
    }
    mueckenVerschwinden();
    mueckenbewegen();
    bildschirmAktualisieren();
    if( !pruefeSpielende() ){
        if( !pruefeRundenende() ){
            handler.postDelayed(this, DELAY_MILLIS);
        }
    }
}

private void eineMueckeAnzeigen(){
    int breite = spielbereich.getWidth();
    int hoehe = spielbereich.getHeight();
    int muecke_breite = Math.round(massstab * 42);
    int muecke_hoehe = Math.round(massstab * 50);
    int links = zufallsgenerator.nextInt(breite - muecke_breite);
    int oben = zufallsgenerator.nextInt(hoehe - muecke_hoehe);

    ImageView muecke  = new ImageView(this);

// muecke.setImageResource(R.drawable.biene);
muecke.setOnClickListener(this);

    int vx;
    int vy;
    double faktor = 1.0;
    //Falls die Geschwindikeit 0 ist
    do{
        vx = zufallsgenerator.nextInt(3) - 1;
        vy = zufallsgenerator.nextInt(3) - 1;
    }while(vx==0 && vy==0);
    setzeBild(muecke, vx, vy);
    if(vx != 0 && vy != 0){
        faktor = 0.70710678;
    }
    vx = (int) Math.round(massstab * vx * faktor);
    vy = (int) Math.round(massstab * vy * faktor);

    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(muecke_breite, muecke_hoehe);
    params.leftMargin = links;
    params.topMargin = oben;
    params.gravity = Gravity.TOP + Gravity.LEFT;//links oben 51

    spielbereich.addView(muecke, params);
    muecke.setTag(R.id.geburtsdarum, new Date());
    muecke.setTag(R.id.vx, new Integer(vx));
    muecke.setTag(R.id.vy, new Integer(vy));
    mp.seekTo(0);
    mp.start();
}

private void setzeBild(ImageView biene, int vx, int vy) {
    Log.e("Mückenfangen","Bild laden Posotion : " + vx + " / " + vy);
    biene.setImageResource(MUECKEN_BILDER[vy+1][vx+1]);
}

private void mueckenVerschwinden(){
    int nummer = 0;
    while (nummer < spielbereich.getChildCount()){
        ImageView muecke = (ImageView) spielbereich.getChildAt(nummer);

        Date geburtsdatum = (Date) muecke.getTag(R.id.geburtsdarum);
        long alter = (new Date()).getTime() - geburtsdatum.getTime();
        if (alter > HOECHSTALTER_MS) {
            spielbereich.removeView(muecke);
            muecke.setOnClickListener(null);
            muecke.setImageDrawable(null);
            muecke = null;
        } else {
            nummer++;
        }
    }
}

private boolean pruefeSpielende(){
    if( zeit == 0 && gefangeneMuecken < muecken ){
        gameOver();
        return true;
    }
    return false;
}

private boolean pruefeRundenende(){
    if(gefangeneMuecken >= muecken){
        starteRunde();
        return true;
    }
    return false;
}

private void gameOver(){
    Dialog dialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
    dialog.setContentView(R.layout.gameover);
    dialog.show();
    spiellaeuft = false;
}

@Override
public void onClick( View biene ){
    gefangeneMuecken++;
    punkte += 100;
    bildschirmAktualisieren();
    mp.pause();

// spielbereich.removeView( biene );
Animation animationTreffer = AnimationUtils.loadAnimation(this, R.anim.treffer);
animationTreffer.setAnimationListener(new MueckeAnimaionListener(biene));
biene.startAnimation(animationTreffer);
biene.setOnClickListener(null);
biene = null;
}

public interface Runnable{
    public void run();
}

@Override
public void run(){
    zeitHerunterzaehlen();
}

@Override
protected void onDestroy(){
    super.onDestroy();
    mp.release();
}

@Override
protected void onPause() {
    super.onPause();
    handler.removeCallbacks(this);
}

private void mueckenbewegen(){
    int nummer = 0;
    while(nummer < spielbereich.getChildCount()){
        ImageView muecke = (ImageView) spielbereich.getChildAt(nummer);
        int vx = (Integer) muecke.getTag(R.id.vx);
        int vy = (Integer) muecke.getTag(R.id.vy);
        //bewegen
        FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) muecke.getLayoutParams();
        params.leftMargin += vx * runde;
        params.topMargin += vy * runde;
        muecke.setLayoutParams(params);
        nummer++;
    }
}

}

Gruss Renato

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

31.12.2018, 16:18:24 via Website

So ich habe mal den Code getestet.
Er läuft auch ohne die Änderungen.

muecke.setOnClickListener(null);
muecke.setImageDrawable(null);
muecke = null

Aber du hast scheinbar einen Schreibfehler.
In der ids.xml steht bestimmt <item name="geburtsdatum" type="id"/>
und nicht geburtsdarum.

Date geburtsdatum = (Date) muecke.getTag(R.id.geburtsdarum);
long alter = (new Date()).getTime() - geburtsdatum.getTime();

dadurch wird wohl die Mücke nicht richtig gelöscht.

Da du die Erweiterung mit der Camera wie der Autor bei Github nicht machst. wird auch weniger Speicher verbraucht und du kannst auch mehr Mücken anzeigen.

Bei mir weden sie richtig gelöscht, auch ohne die Erweiterungen.

Ps.
Habe auch die Anzahl der Mücken erhöt auf 30 und die Lebenszeit auf 4 sek. gestellt.
Kein Problem.

— geändert am 31.12.2018, 16:32:53

Hilfreich?
Kommentieren
Gelöschter Account
  • Forum-Beiträge: 79

01.01.2019, 10:49:37 via Website

Moin Renato,

Probier mal, in deiner "mueckenVerschwinden()"-Methode noch die recycle Funktion einzubauen wie im Beispiel, bevor du alles auf null setzt:

spielbereich.removeView(muecke);
muecke.setOnClickListener(null);
muecke.getDrawable().getBitmap().recycle(); //oder so ähnlich
muecke.setImageDrawable(null);
muecke = null;

Sieht im ersten Moment vllt komisch aus, das man noch etwas von einem Objekt ausführt, direkt bevor man es auf NULL setzt, aber bei mir hat es damals geholfen.

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

01.01.2019, 11:56:28 via Website

Hallo
ich habe doch schon gesagt das es bei mir nur mit der
spielbereich.removeView(muecke);
ohne Probleme funktioniert hat, habe mindestens eine halbe Stunde gespielt und auch die Anzahl der Mücken erhöht.
Allso lag es an dem Schreibfehler das die alten Mücken nicht gelöscht wurden.

Der Code in dem Buch ist Ok.
Hätte mich auch gewundert wenn der nicht funktionirt hätte.
Schreibfehler kommt schon mal vor, aber so extrem logische fehler doch sehr selten.

Frohes neues Jahr wünsche ich allen.

— geändert am 01.01.2019, 12:22:52

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

01.01.2019, 13:07:38 via Website

Ich habe den Fehler gefunden! In der Methode "zeitHerunterzaehlen" hatte ich jeweils zwei Mücken angelegt aber immer nur eine gelöscht. Das führt zwangsläufig zu einem Überlauf des Speichers.

Herzlichen Dank für alle die mir geholfen haben.

Gruss Renato

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

01.01.2019, 17:39:43 via Website

Hallo Renato

Das war natürlich für uns nicht ersichtlich.

Aber selbst wenn du zweimal die Methode „eineMueckeAnzeigen()“ in der „zeitHerunterzaehlen()“ aufrufst.
Sollte das in der mueckenVerschwinden() trotzdem richtig gelöscht werden.

Wenn aber das Tag falsch ist, wird die zweite natürlich nicht gelöscht werden.

In der mueckenVerschwinden() werden ja alle existierenden Mücken überprüft und wenn zu alt gelöscht somit hätte deine zweite auch gelöscht werden müssen.

Konten wir aber nicht sehen da wir den Cod nicht hatten, und der letzte war Ok.
Auch mit dieser R.id.geburtsdarum ID hätte es funktioniert wenn du auch diese ID in der Xml-Datei erstellt wäre. Ansonsten hättest du auch einen Compiler Fehler gehabt.

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

02.01.2019, 12:05:40 via Website

Ciao Jokel

Da hast du Recht. Ich verstehe es auch nicht ganz. Aber auf jeden Fall funktioniert es, wenn ich den ersten Aufruf entferne.

Gruss Renato

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

02.01.2019, 13:15:06 via Website

Ich verstehe es auch nicht ganz. Aber auf jeden Fall funktioniert es, wenn ich den ersten Aufruf entferne.

welche Zeile genau hast du entfernt?

Zeige mal bitte deine zeitHerunterzaehlen() Methode so wie sie jetzt bei dir ist, wo es geht.

Also ich verstehe die Methode so.

Bei jedem 10. Durchlauf können Mücken erzeugt werden (jede sek.)
if(zeit % (1000/DELAY_MILLIS) == 0)

nun abhängig von der Zufallszahl werden in den Runden 1-6 immer zwei Mücken erstellt.
Ab Runde 7-12 werden auch zwei Mücken erstellt
Ab 12 werden drei Mücken gleichzeitig erstellt.

Der sinn darin erschließt sich mir nicht so recht.
Runde 1-6 eine Mücke pro sek.
Runde 7-12 zwei Mücken pro sek.
Ab der 12 Runde 3 Mücken pro sek.
Würde sinn machen dafür müsste aber der Code etwas geändert werden.

Die Angaben beruhen auf deinen Konstanten.
private static final long HOECHSTALTER_MS = 2000;
public static final int DELAY_MILLIS = 100;
public static final int SPIEL_ZEIT = 900;
public static final int SPEIL_FAKTOR = 300;
public static final float ANZAHL_BIENEN = 15f;

— geändert am 02.01.2019, 14:13:42

Hilfreich?
Kommentieren
Robbiani Renato
  • Forum-Beiträge: 602

06.01.2019, 12:43:25 via Website

Ciao Jokel

Die Methode zeitHerunterzählen sieht bei mir wie folgt aus:

    private void zeitHerunterzaehlen(){
    zeit--;

    if(zeit % (1000/DELAY_MILLIS) == 0) {

        float zufallszahl = zufallsgenerator.nextFloat();
        if (zufallszahl < muecken * ANZAHL_BIENEN / SPIEL_ZEIT) {

            double wahrscheinlichkeit = muecken * ANZAHL_BIENEN / SPIEL_ZEIT;
            if (wahrscheinlichkeit > 1) {
                eineMueckeAnzeigen();
                if (zufallszahl < wahrscheinlichkeit - 1) {
                    eineMueckeAnzeigen();
                }
            } else {
                if (zufallszahl < wahrscheinlichkeit) {
                    eineMueckeAnzeigen();
                }
            }
        }
    }
    mueckenVerschwinden();
    mueckenbewegen();
    bildschirmAktualisieren();
    if( !pruefeSpielende() ){
        if( !pruefeRundenende() ){
            handler.postDelayed(this, DELAY_MILLIS);
        }
    }
}

Gruss Renato

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

06.01.2019, 15:35:28 via Website

Resultat deines Codes ist . Bis Runde 12 kann jede Sekunde eine Mücke erstellt werden. Ab runde 12 bis runde 18 können zwei Mücken erstellt werden.
Ab runde 18 wieder nur eine Mücke.

Finde das etwas komisch und auch die Art wie es programmiert ist . Hätte man einfacher machen können.
Ok wenn es deinen Vorstellungen entspricht.

Aber das hat eigentlich wenig mit deinem eigentlichen Problem zu tun.

Wenn dein Code wirklich so ist, wie in dem vorletzten Post, verstehe ich nicht warum die app abstürzt, wenn zwei Mücken gleichzeitig in einem Zyklus erzeugt werden. Bei mir werden drei oder sogar vier Mücken gleichzeitig in einem Zyklus erzeugt und ich bekomme kein outofMemory.

Wie gross MB sind den deine drawable?

— geändert am 06.01.2019, 17:04:33

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.527

06.01.2019, 16:36:52 via Website

In der Methode "zeitHerunterzaehlen" hatte ich jeweils zwei Mücken angelegt aber immer nur eine gelöscht. Das führt zwangsläufig zu einem Überlauf des Speichers.

Kann nicht der Fehler sein denn es werden immer alle Mücken die älter als 2 sek. sind gelöscht. Und das auch mit einem Aufruf der Methode mueckenVerschwinden.
Denn darin ist eine schleife die alle alten Mücken löscht.

Außer deine Bilder,drawable sin sehr gross dann könnte es sein das es zu einem Speicher überlaufen kommt.

Wenn nicht stimmt etwas mit dem löschen nicht. Vielleicht hast du einem schreib Fehler bei dem Tag.

— geändert am 06.01.2019, 16:48:56

Hilfreich?
Kommentieren