Bitmap-App, Exception "Out of memory"

  • Antworten:5
  • OffenNicht stickyNicht beantwortet
  • Forum-Beiträge: 37

01.11.2016 16:06:38 via Website

Hallo,

  • meine App
    3 Bilder aus der Galerie meines Gerätes sollen in einer GridView angezeigt werden.
    Die Bilder sind ziemlich gross (ca. 400 kb)
    Zuerst sind das die Thumbnails der Bilder, nach Klick auf eines wird das Original-Bild
    bildschirmfüllend angezeigt.
    GridView wird mit einem Adapter verbunden, der die Thumbnails erzeugt und die
    Bilder komprimiert um den Speicherbedarf des Bildes zu verringern.
    Die Anzeige des vollen Bildes erfolgt mit Hilfe einer AsyncTask.
  • Ergebnis
    Es funktioniert in dieser Reihenfolge:
    o Anzeige der Thumbnails
    o Klick auf ein Thumbnail
    o Anzeige des Originalbildes
    o Klick auf BackPressed
    Nun kann das Ganze problemlos wiederholt werden.
  • Problem
    Wenn ich die App nun beende und danach erneut aufrufe kommt folgender Fehler:
    11-01 15:20:32.481 499-499/de.carpelibrum.bildergalerie E/AndroidRuntime? FATAL EXCEPTION: main
    java.lang.OutOfMemoryError
    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:621)
    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:387)
    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:420)
    at de.carpelibrum.bildergalerie.BildAdapterSd.bilderLesen(BildAdapterSd.java:49)
    at de.carpelibrum.bildergalerie.BildAdapterSd.(BildAdapterSd.java:65)
    at de.carpelibrum.bildergalerie.BildergalerieActivity.onResume(BildergalerieActivity.java:46)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1199)
    at android.app.Activity.performResume(Activity.java:5121)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2611)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2649)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2122)
    at android.app.ActivityThread.access$700(ActivityThread.java:134)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1218)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4867)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
    at dalvik.system.NativeStart.main(Native Method)
    11-01 15:20:41.239 499-499/de.carpelibrum.bildergalerie I/Process? Sending signal. PID: 499 SIG: 9

    Die Fehleranweisung ist:
    bm= BitmapFactory.decodeFile(fileObj.getAbsolutePath());

  • Rufe ich die nun ein 3. mal funktioniert sie wieder, usw. ...

Codeteile:

public class BildergalerieActivity extends Activity implements AdapterView.OnItemClickListener
{
BaseAdapter adapter; //BildAdapter oder BildAdapterSd
BildAdapterSd basd;
GridView galerie;
boolean as;
boolean adap=false; //true/false Bilder von R.drawable/sdcard
ImageView bild;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
galerie = (GridView) findViewById(R.id.gridview);
bild = (ImageView) findViewById(R.id.imageView1);
bild.setLongClickable(true);
Log.d("* BilderA4-Thread *", Thread.currentThread().getName());
galerie.setVisibility(View.VISIBLE);
//vom Einzelbild zurück zum Anfang
bild.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View arg1) {
bild.setVisibility(View.INVISIBLE);
galerie.setVisibility(View.VISIBLE);
as = false;
return true;
}
});
}

// @Override
protected void onResume() {
super.onResume();
Log.d("* Sd *","BildergalerieActivity, Sd");
adapter = new BildAdapterSd(this);
basd=(BildAdapterSd)adapter;
galerie.setAdapter(adapter);
galerie.setOnItemClickListener(this);
}

@Override
public void onItemClick(AdapterView parent, View v, int pos, long id) {
int resid;
// if (adap)
// resid= (Integer) adapter.getItem(pos);
// else
resid=pos;
if (true) { //Einzelbild in AsyncTask
EinzelbildActivityAsync eb = new EinzelbildActivityAsync();
galerie.setVisibility(View.INVISIBLE);
bild.setVisibility(View.VISIBLE);
eb.execute(new Integer(resid), this);
}
}

@Override
public void onBackPressed() {
// ClearBm();
if (as) {
as=false;
Log.d("* onBackPressed ", " return from Async");
bild.setVisibility(View.INVISIBLE);
galerie.setVisibility(View.VISIBLE);
} else {
super.onBackPressed();
}
}
void ClearBm(String p) { //Löschen der Bitmaps
Bitmap bmm;
int j=0;
if (adapter != null && adapter instanceof BildAdapterSd) {
basd = (BildAdapterSd) adapter;
for (int i = 0; i < basd.list.size(); i++) {
bmm = basd.list.get(i);
if (bmm != null) {
basd.list.get(i).recycle();
bmm = null;
j++;
}
bmm = basd.list2.get(i);
if (bmm != null) {
basd.list2.get(i).recycle();
bmm = null;
j++;
}
}
basd.list.clear();
basd.list2.clear();
}
basd=null;
Log.d("
ClearBm ",p+" Anzahl:"+j);
}
public void onDestroy() {
if (true) {
ClearBm("oD");
galerie.destroyDrawingCache();
galerie.removeAllViewsInLayout();
Log.d("NBGA
* ", getLocalClassName() + ".onDestroy(), galerie.removeAllViewsInLayout() gerufen.");
}
super.onDestroy();
}
}

class BildAdapterSd extends BaseAdapter {
private Context context;
public List list = new ArrayList();
public List list2 = new ArrayList();
Integer[] bilderIDs;

File fileObj;
Bitmap bitMapObj;
Bitmap bmm;
void bilderLesen() {
((BildergalerieActivity)context).ClearBm("bL");
File sd= Environment.getExternalStorageDirectory();
String s=sd.getPath()+"/DCIM/browser-photos";
File sd2=new File(s);
File[] dsn=sd2.listFiles();
Bitmap thumbnail=null, bm=null;
for (int i=0;i long ll=dsn[i].length();
// if (dsn[i].getPath().indexOf("_") > 0) //Originalbilder weglassen
if (ll < 80000) //nur große Originalbilder
continue;
fileObj = new File(dsn[i].getPath());
Log.d("* Name,Länge ",dsn[i].getPath()+","+ll);
System.gc();
int THUMBSIZE = 128;
//
********* bewirkt "java.lang.OutOfMemoryError" *********
bm= BitmapFactory.decodeFile(fileObj.getAbsolutePath());
//http://stackoverflow.com/questions/18545246/how-to-compress-image-size
bm=Bitmap.createScaledBitmap(bm, 600, 600, true); //komprimieren, Werte probiert
thumbnail = ThumbnailUtils.extractThumbnail(
bm,
THUMBSIZE,
THUMBSIZE);
list2.add(bm);
list.add(thumbnail);
int j=0;
}

}

public BildAdapterSd(Context c) {//throws OutOfMemoryError {
context = c;
bilderLesen();
}

@Override
public int getCount() { return list.size(); }

@Override
public Object getItem(int position) { return list.get(position); }

@Override
public long getItemId(int position) { return 0; }

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView iv;
//convertView: The old view to reuse, if possible. ...
if (convertView == null) { // if it's not recycled, initialize some attributes
iv = new ImageView(context);
iv.setLayoutParams(new GridView.LayoutParams(150, 100));
iv.setPadding(5, 5, 5, 5);
/* Scale the image uniformly (maintain the image's aspect ratio) so that
both dimensions (width and height) of the image will be equal to or
larger than the corresponding dimension of the view (minus padding).
*/
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
iv = (ImageView) convertView;
}
iv.setImageBitmap(list.get(position));
return iv;
}
}

Bitte um Hilfe !
Gruß Wicki

  • Forum-Beiträge: 2.214

01.11.2016 16:29:09 via Website

Hallo Wicki,

leider kann dein Code so nicht funktionieren - denn Bitmaps werden vom Renderer immer der displayauflösung angepasst ... (dpi) - dein BaseAdapter wird auf kurz oder lang somit auch das Zeitliche segnen

1) Du musst je nach Anzahl das auch in Threads verpacken und das Memory-Management auf LruCache umstellen

2) Bitte beachte , dass ein Bitmap lediglich eine Kopie deines komprimierten Files ist und erst beim Rendern entpackt wird. Dein Scaling musst du ergo unbedingt erst im Thread ausführen.

Abgekürzt : Du benötigst einen BaseAdapter mit dynamischem Laden im Thread incl. LruCache Verwaltung

— geändert am 01.11.2016 17:26:43

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

  • Forum-Beiträge: 37

01.11.2016 16:45:39 via Website

Ich weiß nicht ob ich Dich richtig verstanden habe, mit "drawable-nodpi ordner" meinst Du wohl einen Ordner im App-Project.
Meine Bilder werden jedoch während der Laufzeit vom Smartphone eingelesen (siehe Adapter-Klasse, Methode bilderLesen) und dann entsprechend bearbeitet. Daher stehen sie auch nicht in einem Drawable-Ordner.

Gruß Wicki

  • Forum-Beiträge: 2.214

01.11.2016 16:50:43 via Website

Hallo Wicki ,

ich habe meinen Beitrag oben "zu lange" geändert :-)
Habe erst zu spät gesehen , dass du extern lädst ...

Ergo schau dir nochmal meinen Betrag an ... :-)

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

  • Forum-Beiträge: 37

01.11.2016 17:10:10 via Website

Dank für diese Antwort, die ich erst jetzt gelesen habe. Dieses Lru-Memory-Management ist für mich neu und ich muss mich da erst mal kundig machen.

  • Forum-Beiträge: 2.214

01.11.2016 17:14:31 via Website

Dank für diese Antwort, die ich erst jetzt gelesen habe. Dieses Lru-Memory-Management ist für mich neu und ich muss mich da erst mal kundig machen.

... UND Loadingthreads/Async müssen in den Adapter , in dem du skalierst, sonst wird es auch nicht funktionieren .

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