Modifié:
glide
pour charger des images. Je suis en train Out of Memory Error
.J'ai utilisé un gros tas vrai dans manifeste:
Android:largeHeap="true"
Version Glide:
compile 'com.github.bumptech.glide:glide:3.7.0'
Version appareil/Android:
Version de l'appareil Nexus 6.0
Toutes les images que je reçois de Json seraient de 800 Ko à 1 Mo.
Activity_layout:
<RelativeLayout
Android:id="@+id/home_layout_bottom"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_below="@+id/home_layout_top_recycler"
Android:layout_margin="5dp">
<Android.support.v7.widget.RecyclerView
Android:id="@+id/rv_list_tab_home_recycler"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:clipToPadding="false"
Android:scrollbars="vertical"
Android:visibility="visible" />
<TextView
Android:id="@+id/no_user_posts_item_tv_recycler"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_below="@+id/rv_list_tab_home_recycler"
Android:layout_marginTop="80dp"
Android:layout_centerHorizontal="true"
Android:text="@string/txt_no_posts_available"
Android:textColor="@color/txt_common_black"
Android:textSize="@dimen/txt_size" />
</RelativeLayout>
code adaptateur:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
final HomePostItems rowItem = getItem(position);
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.lv_adapter_post_items_layout, null);
holder = new ViewHolder();
holder.ivPostedImage = (ImageView) convertView.findViewById(R.id.iv_posted_img);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
..................
Glide.with(context).load(rowItem.getPosteduserpostimage())
.placeholder(R.drawable.golive_load_image).error(R.drawable.golive_cancel_image)
.override(600, 200)
.into(holder.ivPostedImage);
adapter_layout.xml:
<RelativeLayout
Android:id="@+id/rl_lv_user_post_adapter_img_holder_home"
Android:layout_width="match_parent"
Android:layout_height="300dp"
Android:layout_marginLeft="1dp"
Android:layout_marginRight="1dp"
Android:layout_below="@+id/tv_user_posted_msg_post_items_home" >
<ImageView
Android:id="@+id/iv_posted_img_home"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_centerInParent="true"
Android:scaleType="fitXY"
Android:background="#ffffff"
Android:contentDescription="@string/cont_desc"/>
</RelativeLayout>
Logcat:
Request threw uncaught throwable
Java.util.concurrent.ExecutionException: Java.lang.OutOfMemoryError: Failed to allocate a 6365196 byte allocation with 865912 free bytes and 845KB until OOM
at Java.util.concurrent.FutureTask.report(FutureTask.Java:94)
at Java.util.concurrent.FutureTask.get(FutureTask.Java:164)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor.afterExecute(FifoPriorityThreadPoolExecutor.Java:96)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1121)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:588)
at Java.lang.Thread.run(Thread.Java:818)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.Java:118)
Caused by: Java.lang.OutOfMemoryError: Failed to allocate a 6365196 byte allocation with 865912 free bytes and 845KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at Android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at Android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.Java:635)
at Android.graphics.BitmapFactory.decodeStream(BitmapFactory.Java:611)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.Java:329)
at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.Java:220)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.Java:153)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.Java:50)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.Java:19)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.Java:39)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.Java:20)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeBitmapWrapper(GifBitmapWrapperResourceDecoder.Java:121)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeStream(GifBitmapWrapperResourceDecoder.Java:94)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.Java:71)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.Java:61)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.Java:22)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSourceData(DecodeJob.Java:190)
at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.Java:177)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.Java:128)
at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.Java:122)
at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.Java:101)
at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.Java:58)
at Java.util.concurrent.Executors$RunnableAdapter.call(Executors.Java:423)
at Java.util.concurrent.FutureTask.run(FutureTask.Java:237)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1113)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:588)
at Java.lang.Thread.run(Thread.Java:818)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.Java:118)
Je ne sais pas comment résoudre ce problème de MOO. Veuillez partager votre suggestion, si vous connaissez déjà ce problème.
Ce n'est pas une solution exacte à votre problème, mais vous devez garder ces choses à l'esprit lors du chargement des images dans une liste à l'aide de Glide.
La principale partie menaçante de votre problème est la taille de l'image. L'image que vous obtenez fait presque 1 Mo chacune! Ce qui est en fait trop grand pour les afficher dans une liste de plus de 300 articles. Donc, si vous faites également du côté serveur, il est toujours recommandé d'avoir les images dans plusieurs tailles différentes.
Par exemple, en cas d'afficher une liste d'amis avec leurs photos de profil, je suggère que vous obteniez d'abord la liste complète sur le serveur. Récupérez ensuite toutes les images de profil et stockez-les localement. Remplissez ensuite le ListView
. Et la partie la plus importante est lors du téléchargement d'une photo de profil d'un utilisateur sur le serveur, après l'avoir téléchargée, le serveur doit en conserver plusieurs tailles, par exemple version basse, moyenne et haute résolution. Ainsi, lors de la diffusion des URL de l'image de profil pour ListView
, le serveur peut fournir les images en basse résolution car elles seront probablement utilisées pour les miniatures.
Utiliser RecyclerView
au lieu de ListView
est également un bon appel. Mais cela ne résoudra pas le problème que vous avez ici lorsque vous êtes dans un appareil bas de gamme.
OMM
n'a rien à voir avec ce que vous pouvez résoudre par programme. Vous devez redimensionner votre image dans une version de résolution inférieure.
Vous pouvez également vérifier le Glide mécanisme de mise en cache aussi. Je vous suggère d'utiliser la stratégie de mise en cache afin que chaque fois que vous n'ayez pas à charger l'image depuis le serveur.
Bonne chance.
L'utilisation de Glide ne garantit aucun Out of Memory
erreurs, vous devez utiliser plusieurs petites étapes pour réduire la probabilité de ne pas obtenir OOM's
.
Étape 1: Comprendre le mécanisme de mise en cache dans Glide
Étape 2: Je préfère charger thumbnails
dans recyclerview
Glide
.with( context )
.load( UsageExampleGifAndVideos.gifUrl )
.thumbnail( 0.1f )
.into( imageView2 );
N'oubliez pas de toujours demander une image de petite taille si des images plus grandes ou HD ne sont pas nécessaires.
Utilisez recyclerView
au lieu de ListView
. Il élément unique réutilisable pour le rendu des éléments. J'utilise glide
avec recyclerView
où je charge des fonds d'écran avec plus de 100 éléments.
Dans ListView chaque fois que vous créez une vue, si vous avez 100+ vues et cela créera 100+ vues où comme dans recyclerview il crée combien d'éléments visibles sont là dans l'écran +2.
J'ai fait face au même problème. Je partage la façon dont je l'ai résolu. Créez un dossier nommé drawable-nodpi
Placez vos fichiers golive_load_image
Et golive_cancel_image
Dans ce dossier, et supprimez ces deux fichiers image d'un autre endroit comme drawable-ldpi
, drawable-hdpi
Etc (si vous en avez). Et ajoutez skipMemoryCache( true )
Glide.with(context).load(rowItem.getPosteduserpostimage())
.skipMemoryCache( true )
.placeholder(R.drawable.golive_load_image).error(R.drawable.golive_cancel_image)
.override(600, 200)
.into(holder.ivPostedImage);
La raison est que lors du défilement, glide continue de faire le processus d'image, même les vues associées sont supprimées de la liste. Ajoutez ce code dans le onScrollStateChanged de votre listview.
if (view.getContext() != null) {
switch (scrollState) {
case SCROLL_STATE_IDLE:
Glide.with(view.getContext()).resumeRequests();
break;
case SCROLL_STATE_TOUCH_SCROLL:
case SCROLL_STATE_FLING:
Glide.with(view.getContext()).pauseRequests();
break;
}
}
Une approche différente peut probablement être adoptée pour résoudre ce problème. Pour ce faire, vous pouvez utiliser un autre ImageAdapter avec
Glide.with(mActivity).loadFromMediaStore(_imageInfo.getmUri())
cela ne plante pas en utilisant MediaStoreThumbFetcher
Pour avoir plus de contrôle sur la charge, procédez comme suit avec Glide v4
// usage:
Glide.with(mActivity).load(_imageInfo)....
// in GlideModule.registerComponents
registry.prepend(ImageInfo.class, ImageInfo.class, new UnitModelLoader.Factory<ImageInfo>());
registry.prepend(ImageInfo.class, Bitmap.class, new ImageInfoBitmapDecoder(context));
class ImageInfoBitmapDecoder implements ResourceDecoder<ImageInfo, Bitmap> {
private final ContentResolver contentResolver;
private final BitmapPool pool;
public ImageInfoBitmapDecoder(Context context) {
this.contentResolver = context.getContentResolver();
this.pool = Glide.get(context).getBitmapPool();
}
@Override public boolean handles(ImageInfo source, Options options) { return true; }
@Override public @Nullable Resource<Bitmap> decode(ImageInfo source, int width, int height, Options options) {
Bitmap thumb = Thumbnails.getThumbnail(contentResolver, source.getmId(), Thumbnails.MINI_KIND, null);
return BitmapResource.obtain(thumb, pool);
}
}
En utilisant les API suivantes, nous pouvons déterminer la mémoire libre laissée et la taille du bitmap
Vous pouvez vérifier la mémoire disponible et les détails du bitmap (si nécessaire) en tant que pré-vérification
Vérifiez la quantité de mémoire libre restante
public static final float BYTES_IN_MB = 1024.0f * 1024.0f;
public static float megabytesFree() {
final Runtime rt = Runtime.getRuntime();
final float bytesUsed = rt.totalMemory();
final float mbUsed = bytesUsed/BYTES_IN_MB;
final float mbFree = megabytesAvailable() - mbUsed;
return mbFree;
}
public static float megabytesAvailable() {
final Runtime rt = Runtime.getRuntime();
final float bytesAvailable = rt.maxMemory();
return bytesAvailable/BYTES_IN_MB;
}
Vérifiez la taille du bitmap que nous voulons charger
private void readBitmapInfo() {
final Resources res = getActivity().getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, R.drawable.brasil, options);
final float imageHeight = options.outHeight;
final float imageWidth = options.outWidth;
final String imageMimeType = options.outMimeType;
Log.d(TAG, "w,h, type:"+imageWidth+", "+imageHeight+", "+imageMimeType);
Log.d(TAG, "estimated memory required in MB: "+imageWidth * imageHeight * BYTES_PER_PX/MemUtils.BYTES_IN_MB);
}
Pour plus de détails, passez par méthodes Java pour vérifier la mémoire et le bitmap et discussion github
Afin d'éviter une erreur de mémoire insuffisante, vous pouvez simplement prendre des précautions pour vous assurer qu'elle ne se produira pas. Donc, la réponse à cette question est en fait un tas de suggestions que l'on peut suggérer. Moi aussi.
Comme suggéré par @ Reaz Murshed je recommande également d'avoir les images dans plusieurs tailles différentes. En dehors de cela, je voudrais ajouter quelques éléments supplémentaires qui pourraient vous aider à analyser ce problème et à le résoudre.
Si je me souviens bien, OOM a toujours été une erreur d'utilisation, largeHeap
ne fera que la retarder; ou si c'est une charge importante, ce n'est peut-être pas possible. Je vous suggère donc de suivre ce lien pour diagnostiquer les fuites de mémoire.
Les traces de pile de
OutOfMemoryErrors
n'aident pas du tout à les diagnostiquer. Il vous dit juste qu'il est cassé et que quelque chose a rempli la mémoire. Ce remplissage se produit bien avant que l'exception réelle ne soit levée. Cela implique également qu'en général, tout ce qui jette leOOM
n'est pas réellement le coupable. La seule exception à cela est lorsque la quantité de mémoire allouée est simplement trop grande, par exemple: un tableau à allouer est plus grand que la mémoire maximale, alors vous savez que certains calculs se sont vraiment mal passés, comme le ferait une image 32000x32000 @ 4 prendre environ 4 Go de mémoire.Si vous pouvez reproduire: obtenez un vidage de tas et analysez l'utilisation de votre application. Étapes de diagnostic normales du MOO:
Reproduire l'exception (attendez de la voir dans LogCat)
Prenez un vidage de tas (pour analyser les fuites de mémoire)
Analysez-le pour les gros objets et les fuites
Dans le lien partagé ci-dessus, il y a peu d'autres liens concernant how to take heap dump?
et issues identiques à celui-ci.
Je vous suggère donc d'analyser les fuites de mémoire et de prendre les mesures nécessaires pour éviter les MOO.
J'espère que cela vous aidera.
.thumbnail(...f)
).skipMemoryCache(true)
si vous n'êtes pas obligé de conserver les images dans le cache.diskCacheStrategy(DiskCacheStrategy.NONE)
pour désactiver le cache disque