para - manual de programacion android pdf
Problema de falta de memoria ImageView problema (8)
Las imágenes vienen en todas las formas y tamaños. En muchos casos, son más grandes que los requeridos para una interfaz de usuario (UI) de aplicación típica. Por ejemplo, la aplicación Galería del sistema muestra fotos tomadas con la cámara de su dispositivo Android, que suelen tener una resolución mucho más alta que la densidad de pantalla de su dispositivo.
Dado que está trabajando con memoria limitada, idealmente solo desea cargar una versión de menor resolución en la memoria. La versión de menor resolución debe coincidir con el tamaño del componente UI que la muestra. Una imagen con una resolución más alta no proporciona ningún beneficio visible, pero aún ocupa una memoria valiosa e incurre en una sobrecarga de rendimiento adicional debido a la ampliación adicional de la escala.
Fuente: carga de grandes mapas de bits de manera eficiente
Según la información anterior, te recomendaría en lugar de configurar la imagen de esta manera:
setImageResource(resId);
para establecerlo así:
setScaledImage(yourImageView, resId);
y Copie y pegue los métodos a continuación:
private void setScaledImage(ImageView imageView, final int resId) {
final ImageView iv = imageView;
ViewTreeObserver viewTreeObserver = iv.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
iv.getViewTreeObserver().removeOnPreDrawListener(this);
int imageViewHeight = iv.getMeasuredHeight();
int imageViewWidth = iv.getMeasuredWidth();
iv.setImageBitmap(
decodeSampledBitmapFromResource(getResources(),
resId, imageViewWidth, imageViewHeight));
return true;
}
});
}
private static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds = true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Soy nuevo en la programación de Android y recibí un error que dice que a mi aplicación se le acabó la memoria, esto ejemplificó que copié de un libro y funciona con resolución de imágenes pequeña, pero cuando agregué algunas imágenes con una mayor resolución de error de memoria aparece, puedo hacer algo mal o simplemente no sé todo lo que debería trabajar con las imágenes, si alguien sabe qué debo cambiar para que este error no vuelva a aparecer, ruega ayuda. ¡Gracias anticipar!
El código fuente:
public class ImageViewsActivity extends Activity {
//the images to display
Integer[] imageIDs={
R.drawable.pic1,
R.drawable.pic2,
R.drawable.pic3,
R.drawable.pic4,
R.drawable.pic5
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView iv=(ImageView) findViewById(R.id.image1);
Gallery gallery=(Gallery) findViewById(R.id.gallery);
gallery.setAdapter(new ImageAdapter(this));
gallery.setOnItemClickListener(new OnItemClickListener(){
public void onItemClick(AdapterView<?> parent, View v, int position, long id){
Toast.makeText(getBaseContext(), "pic"+(position+1)+" selected", Toast.LENGTH_SHORT).show();
//display the image selected
try{iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
iv.setImageResource(imageIDs[position]);}catch(OutOfMemoryError e){
iv.setImageBitmap(null);
}
}
});
}
public class ImageAdapter extends BaseAdapter{
private Context context;
private int itemBackground;
public ImageAdapter(Context c){
context=c;
//setting the style
TypedArray a = obtainStyledAttributes(R.styleable.Gallery1);
itemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0);
a.recycle();
}
//returns the number of images
public int getCount() {
// TODO Auto-generated method stub
return imageIDs.length;
}
//returns the ID of an item
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
//returns the ID of an item
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
//returns an ImageView view
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ImageView iv= new ImageView(context);
iv.setImageResource(imageIDs[position]);
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setLayoutParams(new Gallery.LayoutParams(150,120));
iv.setBackgroundResource(itemBackground);
return iv;
}
}}
ERROR AQUÍ:
04-18 10:38:31.661: D/dalvikvm(10152): Debugger has detached; object registry had 442 entries
04-18 10:38:31.661: D/AndroidRuntime(10152): Shutting down VM
04-18 10:38:31.661: W/dalvikvm(10152): threadid=1: thread exiting with uncaught exception (group=0x4001d820)
04-18 10:38:31.691: E/AndroidRuntime(10152): FATAL EXCEPTION: main
04-18 10:38:31.691: E/AndroidRuntime(10152): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.nativeCreate(Native Method)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:499)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:466)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:371)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:539)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:508)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:365)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:728)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.loadDrawable(Resources.java:1740)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.getDrawable(Resources.java:612)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.resolveUri(ImageView.java:520)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.setImageResource(ImageView.java:305)
04-18 10:38:31.691: E/AndroidRuntime(10152): at image.view.GalleryView$ImageAdapter.getView(GalleryView.java:95)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.makeAndAddView(Gallery.java:776)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.fillToGalleryLeft(Gallery.java:695)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.trackMotionScroll(Gallery.java:406)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery$FlingRunnable.run(Gallery.java:1397)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.handleCallback(Handler.java:618)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.dispatchMessage(Handler.java:123)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Looper.loop(Looper.java:154)
04-18 10:38:31.691: E/AndroidRuntime(10152): at android.app.ActivityThread.main(ActivityThread.java:4668)
04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invokeNative(Native Method)
04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invoke(Method.java:552)
04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:917)
04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
04-18 10:38:31.691: E/AndroidRuntime(10152): at dalvik.system.NativeStart.main(Native Method)
Google tiene la respuesta correcta (perfecta):
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Un ejemplo de cómo lo uso en fragmentos:
private ImageView mImageView;
private View view;
private int viewWidth;
private int viewHeight;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_episode_list, container, false);
mImageView = (ImageView) view.findViewById(R.id.ImageView);
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
viewWidth = view.getMeasuredWidth();
viewHeight = view.getMeasuredHeight();
mImageView.setImageBitmap(Methods.decodeSampledBitmapFromResource(getResources(),
R.drawable.YourImageName, viewWidth, viewHeight));
}
});
}
return view;
}
Puse estos métodos de Google en mi clase "Métodos" (a cualquier otro método útil):
public class Methods {
...
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
Notas adicionales a la respuesta de @Sakiboy. Aunque probablemente sea demasiado tarde para mi respuesta, pero esta es mi solución, creo que funciona sin la necesidad de hacer muchos cambios de código.
- Usa
Glide
para manejar todo el almacenamiento en caché. - Para borrar más memoria, debe eliminar manualmente todas las
views
y establecer cualquier mapa de bits / drawable deImageView
paranull
y borrar todos los manejadores de eventos y oyentes. - Establezca
null
todas las variables que tiene en suactivity
ofragment
. - Debes poner tu lógica dentro de
onDestroy
y deberías estar listo para continuar. - El paso opcional es agregar
System.gc()
al final de su código.
Después de borrar todo lo que mencioné anteriormente. Notarás que la memoria disminuirá cada vez que se destruya un fragmento / actividad.
Para agregar la respuesta de Ken, que es una pieza sólida de código, pensé que la derribaría después de configurarla:
if(imageView != null) {
((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
}
imageView = (ImageView) view.findViewById(R.id.imageView);
imageView.setImageResource(resID);
NOTA: Esto no funcionará si está tratando de cambiar una imagen que ya recicló. Obtendrás algo como esto en LOGCAT
Lienzo: tratando de usar un mapa de bits reciclado
Entonces, ¿qué hago ahora si no tengo que cargar un montón de imágenes diferentes de forma asíncrona? Simplemente pongo esto en Destroy cuando trato con fragmentos y grandes imágenes de fondo:
@Override
public void onDestroy() {
super.onDestroy();
imageView.setImageDrawable(null);
}
Para aquellos que usan la biblioteca de carga de imágenes Glide , que todavía están teniendo estos OutOfMemory Exception
, hay muchas cosas que puede hacer para que Glide
utilice menos memoria y, con suerte, solucione su problema. Éstos son algunos de ellos:
No use
android:scaleType="fitXY"
dentro de suImageView
. Entonces, si eresImageView
tiene este aspecto:<ImageView android:id="@android:id/icon" android:layout_width="@dimen/width" android:layout_height="@dimen/height" android:adjustViewBounds="true" android:scaleType="fitXY" <!-- DON''T USE "fitXY"! --> />
Cambie
ImageView
para usar unandroid:scaleType
diferenteandroid:scaleType
, preferiblemente:fitCenter
ocenterCrop
.- No use
wrap_content
en suImageView
, en su lugar usematch_parent
o especifique elwidth
/height
explícitamente usando un tamaño endp
. Si realmente insiste en usarwrap_content
en suImageView
, al menos configure unandroid:maxHeight
/android:maxWidth
. - Desactiva las animaciones con:
dontAnimate()
en tuGlide.with()...
Si está cargando muchas imágenes potencialmente grandes (como lo haría en una lista / cuadrícula), especifique una carga de
thumbnail(float sizeMultiplier)
en su solicitud. Ex:Glide.with(context) .load(imageUri) .thumbnail(0.5f) .dontAnimate() .into(iconImageView);
Temporalmente baje la huella de memoria de
Glide
durante ciertas fases de su aplicación usando:Glide.get(context).setMemoryCategory(MemoryCategory.LOW)
.- Solo caché en la memoria si es necesario, puede desactivarlo con:
skipMemoryCache(true)
en suGlide.with()...
Esto aún almacenará en caché las imágenes en el disco, lo que probablemente desee ya que está dejando de lado el caché en memoria. - Si está cargando un
Drawable
de sus recursos locales, asegúrese de que la imagen que está tratando de cargar NO ES SUPER ENORME. Hay muchas herramientas de compresión de imágenes disponibles en línea. Estas herramientas reducirán el tamaño de sus imágenes y al mismo tiempo mantendrán la calidad de su apariencia. - Si carga desde recursos locales utilice
.diskCacheStrategy(DiskCacheStrategy.NONE)
. onTrimMemory(int level)
devolución de llamadaonTrimMemory(int level)
que proporciona Android para recortar la cachéGlide
según sea necesario. Ex.@Override public void onTrimMemory(int level) { super.onTrimMemory(level); Glide.get(this).trimMemory(level); }
Si muestra imágenes en un
RecyclerView
puede borrar explícitamenteGlide
cuando las vistas se reciclan, así:@Override public void onViewRecycled(MyAdapter.MyViewHolder holder) { super.onViewRecycled(holder); Glide.clear(holder.imageView); }
- Si esto sigue ocurriendo, incluso después de haber "intentado todo", el problema podría ser su aplicación (¡GASP!), Y
Glide
es lo único que lo está empujando a la zona deOutOfMemory Exception
... Así que asegúrese de no usar No tiene pérdidas de memoria en su aplicación.Android Studio
proporciona herramientas para identificar problemas de consumo de memoria en tu aplicación. - Por último, consulte la página de problemas en Glide''s GitHub , para problemas similares que pueden proporcionar información sobre la solución de su problema (s). El repositorio se maneja muy bien y es muy útil.
Puedes dejarlo en bibliotecas de terceros como Glide
// imageView.setImageResource(imageId);
Glide.with(this) // Activity or Fragment
.load(imageId)
.into(imageView);
A continuación, le indicamos cómo agregarlo a su build.gradle
:
compile group: ''com.github.bumptech.glide'', name: ''glide'', version: ''3.7.0''
Picasso de Square también lo hace Picasso carga recursos extraíbles de su URI
Tuve el mismo problema cuando estaba mostrando una imagen grande en la vista de la imagen en el modo PAISAJISMO. así que me resolvieron usando este código
File imgFile = new File(imageFile.getAbsolutePath()); // path of your file
FileInputStream fis = null;
try {
fis = new FileInputStream(imgFile);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
options.inPurgeable = true;
options.inScaled = true;
Bitmap bm = BitmapFactory.decodeStream(fis, null,options);
profileIV.setImageBitmap(bm);
}
Utilizar
((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
Antes de cambiar a una nueva imagen !!