Java.lang.OutOfMemoryError en almacenar imágenes en sqlite db

Quiero almacenar imágenes en mi base de datos. También quiero comprobar que si la imagen y el título ya está en la base de datos. Si es así, no los agregará a la base de datos. Esta es mi clase.

Atracciones

public class Attractions extends ListActivity { DataBaseHandler db = new DataBaseHandler(this); ArrayList<Contact> imageArry = new ArrayList<Contact>(); List<Contact> contacts; ContactImageAdapter adapter; int ctr, loaded; int [] landmarkImages={R.drawable.oblation,R.drawable.eastwood,R.drawable.ecopark,R.drawable.circle}; String []landmarkDetails = { "Oblation", "Eastwood", "Ecopark", "QC Circle"}; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_attractions); ctr = db.checkContact(landmarkDetails[loaded]); // get image from drawable /** * CRUD Operations * */ // Inserting Contacts Log.d("Insert: ", "Inserting .."); for(loaded=0; loaded <landmarkDetails.length;loaded++){ Bitmap image = BitmapFactory.decodeResource(getResources(), landmarkImages[loaded]); // convert bitmap to byte ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, stream); byte imageInByte[] = stream.toByteArray(); Log.d("Going to load images", "Image "+ loaded); Log.d("Goind to load objects", "loading"); if(ctr == 0){ Log.d("Nothing Loaded", "Loading Now"); db.addContact(new Contact(landmarkDetails[loaded], imageInByte));} Log.d(landmarkDetails[loaded], "Loaded!"); image.recycle(); } loadFromDb(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.attractions, menu); return true; } public void loadFromDb(){ // Reading all contacts from database contacts = db.getAllContacts(); for (Contact cn : contacts) { String log = "ID:" + cn.getID() + " Name: " + cn.getName() + " ,Image: " + cn.getImage(); // Writing Contacts to log Log.d("Result: ", log); //add contacts data in arrayList imageArry.add(cn); } adapter = new ContactImageAdapter(this, R.layout.screen_list, imageArry); ListView dataList = (ListView) findViewById(android.R.id.list); dataList.setAdapter(adapter); } public void onPause(){ super.onPause(); } public void onResume(){ super.onResume(); } } 

Funciona muy bien en el emulador, pero he intentado probar en mi S4 y luego después de 3 intentos de ir a esta clase, obligó a detener. Lo probé con depuración usb y el logcat mostró java.lang.outofmemoryerror . El logcat señaló el error en mi contactimageadapter.

ContactImageAdapter

 public class ContactImageAdapter extends ArrayAdapter<Contact>{ Context context; int layoutResourceId; // BcardImage data[] = null; ArrayList<Contact> data=new ArrayList<Contact>(); public ContactImageAdapter(Context context, int layoutResourceId, ArrayList<Contact> data) { super(context, layoutResourceId, data); this.layoutResourceId = layoutResourceId; this.context = context; this.data = data; } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; ImageHolder holder = null; if(row == null) { LayoutInflater inflater = ((Activity)context).getLayoutInflater(); row = inflater.inflate(layoutResourceId, parent, false); holder = new ImageHolder(); holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle); holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon); row.setTag(holder); } else { holder = (ImageHolder)row.getTag(); } Contact picture = data.get(position); holder.txtTitle.setText(picture._name); //convert byte to bitmap take from contact class byte[] outImage=picture._image; ByteArrayInputStream imageStream = new ByteArrayInputStream(outImage); Bitmap theImage = BitmapFactory.decodeStream(imageStream); holder.imgIcon.setImageBitmap(theImage); return row; } static class ImageHolder { ImageView imgIcon; TextView txtTitle; } } 

Y señaló a esta línea Bitmap theImage = BitmapFactory.decodeStream(imageStream);

Tengo poco (casi ninguno) conocimiento sobre manejar imágenes y almacenarlas. También android:largeHeap pero todavía fuerza se cierra en múltiples intentos. Espero que alguien me ayude a resolver este problema, o al menos me muestre una forma diferente de almacenar texto e imágenes en sqlite db. ¡Muchas gracias!

Tiene varios lugares donde toda la imagen (suponiendo que sea grande) se mantiene en la memoria:

  1. El objeto de contacto lo tiene. Todas las imágenes cargadas están en imageArry, que es una variable de nivel de instancia.

     public class Attractions extends ListActivity { DataBaseHandler db = new DataBaseHandler(this); ArrayList<Contact> imageArry = new ArrayList<Contact>(); 
  2. En el método ContactImageAdapter.getView , se crea otra copia de imagen como BMP en el objeto holder y se pasa del método. Por lo tanto, en algún momento usted no tiene suficiente memoria para mantener todos ellos. También estoy seguro de que decodeStream necesita más memoria para realizar.

Después de todo no es predecible cuando cada nuevo titular creado en getView será limpiado por GC. Por lo general, para tal situación cuando el objeto creado como nuevo en algún método, a continuación, pasó de nuevo al método de llamada, ese objeto será recopilado sólo por GC completa.

Por lo tanto, como "Software Sainath" dijo, no almacenar imágenes en la base de datos … y no mantenerlos en la memoria tampoco.

PS Luego, proporcione a la vista un enlace al archivo de imagen externo. Eso también ahorrará tiempo para cargar una vista. La imagen estará en la caché y si el usuario al menos una vez lo consiguió, no pasará a través de la red de nuevo.

Supongo que las imágenes no suelen cambiar a sí mismos. Otra imagen de Contacto será otro archivo …

Escribí una respuesta al problema algo similar hace algún tiempo, aquí está el acoplamiento que puedes comprobar. El problema está en el acercamiento de guardar las imágenes en la base de datos, usted no debe hacer esto. En su lugar, escriba las imágenes como archivos en la memoria del teléfono y úsela más.

  1. No almacene la imagen a la base de datos de Sqlite eventual, usted funcionó en fuera del error de la memoria después de tres o cinco imágenes ahorradas a la base de datos. No es la mejor práctica, memoria máxima asignada para el campo en una fila en sqlite es menos de 3mb, ser conscientes de esto.

  2. En lugar de guardar imágenes en la base de datos, guarde las imágenes dentro de la carpeta de la aplicación, guarde la ruta de acceso a la base de datos.

  3. Usted está cargando su imagen como está a su adaptador de la imagen. Digamos que su imagen es de 1280×720 de resolución y 2 MB de tamaño, que tendrá el mismo espacio en su memoria Heap.

Puede escalar su imagen y cargarla como mapa de bits a su ImageView de adaptador de esta manera.

Antes de cargar su imagen como Bitmap obtener altura y ancho.

 //Code read the image and give you image height and width.it won't load your bitmap. BitmapFactory.Options option = new BitmapFactory.Options(); option.inJustDecodeBounds = true; BitmapFactory.decodeFile(your_image_url,option); int image_original_width = option.outHeight; int image_original_height = option.outWidth; 

Ahora para bajar su imagen usted tiene que saber el ancho y la altura de ImageView. Esto se debe a que vamos a reducir la imagen que coincide con el imageview con la perfección de píxeles.

 int image_view_width = image_view.getWidht(); int image_view_height = image_view.getHeight(); int new_width; int new_height; float scaled_width; if(image_original_width>image_view_width) { //if the image_view width is lesser than original_image_width ,you have to scaled down the image. scale_value =(float)image_original_width/(float)image_view_width; new_width = image_original_width/scaled_value; new_height = image_orignal_height/scale_value } else { // use the image_view width and height as sacling value widht and height; new_width = image_view_width; new_height = image_view_height; } 

Ahora reduzca su mapa de bits y cargarlo de esta manera.

  // this will load a bitmap with 1/4 the size of the original one. // this to lower your bitmap memory occupation in heap. BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 4; Bitmap current_bitmap = BitmapFactory.decodeFile(image_url,option); Bitmap scaled_bitmap = Bitmap.createScaledBitmap(current_bitmap,new_width,new_height,true); holder.imgIcon.setImageBitmap(scaled_bitmap); //release memory occupied by current_bitmap in heap, as we are no longer using it. current_bitmap.recycle(); 

Si quieres entender un poco más sobre Bitmap y memoria, mira este enlace .

Si no desea gestionar el mapa de bits de reescalado por sí mismo. Puede utilizar Glide o la biblioteca de Picasso que hace lo mismo.

He escrito un artículo sobre el uso de Picasso para cargar la imagen en listview, que le ayudará a comenzar, si usted está mirando para utilizar picasso.

http://codex2android.blogspot.in/2015/11/picasso-android-example.html

Asegúrese de utilizar el tipo de referencia de recolección rápida de basura al cargar las imágenes de la red

 import java.lang.ref.SoftReference; import java.util.Collections; import java.util.HashMap; import java.util.Map; import android.graphics.Bitmap; public class MemoryCache { private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>()); public Bitmap get(String id){ if(!cache.containsKey(id)) return null; SoftReference<Bitmap> ref=cache.get(id); return ref.get(); } public void put(String id, Bitmap bitmap){ cache.put(id, new SoftReference<Bitmap>(bitmap)); } public void clear() { cache.clear(); } } 
  1. No almacene la imagen en la base de datos Sqlite. No es la mejor práctica.
  2. En lugar de guardar imágenes en la base de datos, mantenga las imágenes en un almacenamiento, pero si desea mantenerlas privadas, guárdelas en la carpeta de la aplicación y guarde la ruta de acceso a la base de datos.
  3. Utilice una de las bibliotecas conocidas como http://square.github.io/picasso/ o https://github.com/bumptech/glide , que ofrecen una gran ayuda con problemas de memoria y también algunos efectos de transición cool. Recomiendo usar Glide porque funciona muy bien en dispositivos con restricciones de memoria bajas
  • Cómo recuperar datos de la base de datos sqlite en android y mostrarla en TextView
  • SQLite Base de datos "contexto" pasó a adaptador
  • Cómo utilizar la conexión SQLite desde la carpeta de activos
  • "AS" equivalente en sqlite
  • Android: niveles de aislamiento de transacciones SQLite (ORMLite)
  • IllegalStateException: obtiene la ranura de campo de la fila 0 col -1 falló
  • Evitar duplicar la entrada en la base de datos sqllite android
  • Base de datos no visible en la carpeta DDMS cuando se utiliza el dispositivo real en lugar del emulador
  • SQLiteDatabase.openDatabase vs SQLiteOpenHelper.getReadableDatabase
  • SQLiteDatabase getWritableDatabase () no funciona
  • Android, cómo ejecutar un archivo sql en sqlitedatabase
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.