Problèmes de MOO causés par Bitmap


Introduction à cette section :

Dans la section précédente, nous avons appris l'utilisation de base de Bitmap, et dans cette section, nous allons discuter du problème MOO de Bitmap. Vous avez peut-être rencontré ou non des problèmes de MOO causés par Bitmap lors du développement réel. Dans cette section, nous le ferons. Découvrons ce sujet ~ Comprenons ce qu'est le MOO, pourquoi il provoque le MOO et améliorons les problèmes causés par Bitmap Problèmes de MOO ~


1. Qu'est-ce que le MOO ? Pourquoi cela provoque-t-il un MOO ?

Réponse : Mémoire insuffisante (débordement de mémoire), nous savons tous que le système Android allouera un espace de travail indépendant pour chaque APP. Ou allouez une machine virtuelle Dalvik distincte afin que chaque application puisse fonctionner indépendamment sans s'affecter mutuellement ! Et Android pour chacun Les machines virtuelles Dalvik auront une limite de mémoire maximale si la mémoire actuellement occupée ainsi que les ressources mémoire que nous demandons dépassent cette limite. , le système générera une erreur MOO ! De plus, ne confondez pas cela avec la RAM Même s’il reste plus de 1 Go de mémoire dans la RAM, le MOO se produira toujours ! Ne mélangez pas RAM (mémoire physique) et MOO ! De plus, si la RAM est insuffisante, l'application sera tuée, pas seulement le MOO ! La norme de mémoire maximale dans Dalvik est différente selon les modèles. Vous pouvez appeler :

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());

pour obtenir la norme de mémoire maximale normale, ou taper directement sur la ligne de commande :

adb shell getprop | grep dalvik.vm.heapgrowthlimit

Vous pouvez également ouvrir. le fichier de code source du système /system/build.prop et regardez les informations dans cette partie du fichier pour obtenir :

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

Il y a trois domaines qui nous préoccupent : la taille initiale de la mémoire du tas heapstartsize, et l'application de la norme tasgrowthlimit. Le plus grand tas Taille de la mémoire, heapsize définit la taille maximale de la mémoire du tas pour les applications utilisant Android:largeHeap!

J'ai essayé ici les normes normales d'allocation de mémoire maximale pour plusieurs modèles :

1.png

Vous pouvez également l'essayer sur votre propre machine~

Bon, arrêtons d'en parler. Concernant l'apparition des problèmes de MOO, nous nous arrêterons ici. Ensuite, nous parlerons de la gestion de la mémoire, mais c'est un gros problème. Je n’arrive toujours pas à le mâcher maintenant… Jetons un coup d’œil à quelques conseils pour éviter Bitmap OOM !


2. Résumé des conseils pour éviter le MOO causé par Bitmap


1) Utilisez un codage à faible empreinte mémoire

Comme mentionné dans la section précédenteBitmapFactory.OptionsCette classe, nous pouvons définir l'attribut inPreferredConfig, La valeur par défaut est Bitmap.Config.ARGB_8888, nous pouvons la changer en Bitmap.Config.ARGB_4444
Bitmap.Config ARGB_4444 : chaque pixel occupe quatre bits, c'est-à-dire A= 4, R =4, G=4, B=4, alors un pixel occupe 4+4+4+4=16 bits
Bitmap.Config ARGB_8888 : Chaque pixel occupe huit bits, soit A=8, R =8, G=8, B=8, alors un pixel occupe 8+8+8+8=32 bits
Par défaut, ARGB_8888 est utilisé, c'est-à-dire qu'un pixel occupe 4 octets !


2) La compression d'image

est également BitmapFactory.Options Nous définissons le facteur d'échelle via inSampleSize, par exemple, écrivez 2, c'est-à-dire que la longueur et la largeur deviennent. 1/2 de l'original, l'image fait 1/4 de la taille originale. Si vous ne la mettez pas à l'échelle, réglez-la simplement sur 1 ! Mais il ne peut pas être compressé aveuglément, après tout, cette valeur est trop petite. Sinon, l'image sera très floue et nous devons éviter de l'étirer et de la déformer. Nous devons donc calculer cela dynamiquement dans le programme. La valeur appropriée de inSampleSize, et il existe une telle méthode dans Options : inJustDecodeBounds, définissez ce paramètre sur Après true, decodeFiel n'allouera pas d'espace mémoire, mais pourra calculer la longueur et la largeur de l'image originale, en appelant options.outWidth/outHeight Obtenez la largeur et la hauteur de l'image, puis utilisez un certain algorithme pour obtenir la valeur appropriée inSampleSize, merci pour le code fourni par street god - tiré du blog Hongyang !

public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
        int widthRadio = Math.round(width * 1.0f / reqWidth);
        int heightRadio = Math.round(height * 1.0f / reqHeight);
        inSampleSize = Math.max(widthRadio, heightRadio);
    }
    return inSampleSize;
}

Utilisez ensuite la méthode ci-dessus :

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
options.inSampleSize = computeSampleSize(options,128,128);
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
/* 下面两个字段需要组合使用 */  
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
    bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
        Log.e(TAG, "OutOfMemoryError");
}

3. Recyclez l'image à temps

Si un grand nombre d'objets Bitmap sont référencés et que l'application ne le fait pas. besoin d'afficher toutes les images simultanément. Les objets bitmap qui ne sont pas utilisés temporairement peuvent être Recyclez-le rapidement. Pour certains scénarios où vous connaissez clairement l'utilisation des images, vous pouvez les recycler activement, comme les images sur la page de démarrage, utilisez Une fois terminé, recyclez, encadrez l'animation, chargez-en une, dessinez-en une et relâchez-en une ! Chargez lorsqu'il est utilisé, définissez null ou recyclez directement lorsqu'il n'est pas affiché ! Par exemple : imageView.setImageResource(0); Cependant, dans certains cas, des images spécifiques peuvent être chargées, libérées et rechargées à plusieurs reprises, etc., ce qui est inefficace...


4 Autres méthodes

Je n'ai pas utilisé. les méthodes suivantes. Si vous ne l'avez jamais utilisé auparavant, vous pouvez vérifier les informations pertinentes par vous-même :

1. Gérez simplement les ressources d'image via la référence SoftReference

Créez une table de hachage SoftReference. Lorsque vous utilisez une image, vérifiez d'abord si la table de hachage a une référence logicielle et si l'image dans la référence logicielle est vide. S'il est vide, chargez l'image dans la référence logicielle et ajoutez-la au hashmap. Il n'est pas nécessaire de gérer explicitement le recyclage et la libération des images dans le code, gc gérera automatiquement la libération des ressources. Cette méthode est simple et pratique à manipuler, et peut éviter dans une certaine mesure l'inefficacité du chargement et du relâchement répétés de la méthode précédente. Mais ce n'est pas assez optimisé.

Exemple de code :

private Map<String, SoftReference> imageMap 
                                           = new HashMap<String, SoftReference>();

public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
    SoftReference reference = imageMap.get(imageUrl);
    if(reference != null) {
        if(reference.get() != null) {
            return reference.get();
        }
    }
    final Handler handler = new Handler() {
        public void handleMessage(final android.os.Message msg) {
            //加入到缓存中
            Bitmap bitmap = (Bitmap)msg.obj;
            imageMap.put(imageUrl, new SoftReference(bitmap));
            if(imageCallBack != null) {
                imageCallBack.getBitmap(bitmap);
            }
        }
    };
    new Thread(){
        public void run() {
            Message message = handler.obtainMessage();
            message.obj = downloadBitmap(imageUrl);
            handler.sendMessage(message);
        }
    }.start();
    return null ;
}

// 从网上下载图片
private Bitmap downloadBitmap (String imageUrl) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
        return bitmap ;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } 
}
public interface ImageCallBack{
    void getBitmap(Bitmap bitmap);
}

2. Méthode de mise en cache LruCache + SD

À partir d'Android 3.1, le fonctionnaire fournit également le cache LruCache Perform. traitement lorsque la taille de l'image stockée est supérieure à LruCache Si la valeur est définie, les images les moins utilisées récemment seront recyclées et le système libérera automatiquement la mémoire !

Exemple d'utilisation :

Étapes :

1) Définissez d'abord la taille de la mémoire de l'image mise en cache. Je la règle ici sur la valeur. mémoire du téléphone portable 1/8, Comment obtenir la mémoire du téléphone mobile : int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);

2) Les paires clé-valeur dans LruCache sont l'URL et l'image correspondante

3) Réécrivez une méthode appelée sizeOf, qui renvoie le nombre d'images.

private LruCache mMemoryCache;
private LruCacheUtils() {
    if (mMemoryCache == null)
        mMemoryCache = new LruCache(
                MAXMEMONRY / 8) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }

            @Override
            protected void entryRemoved(boolean evicted, String key,
                    Bitmap oldValue, Bitmap newValue) {
                Log.v("tag", "hard cache is full , push to soft cache");
               
            }
        };
}

4) Les méthodes suivantes consistent à vider le cache, à ajouter des images au cache, à extraire des images du cache et à les supprimer du cache.

Supprimer et vider le cache est indispensable, car une mauvaise gestion du cache d'images entraînera un débordement de mémoire, vous devez donc y faire attention.

public void clearCache() {
    if (mMemoryCache != null) {
        if (mMemoryCache.size() > 0) {
            Log.d("CacheUtils",
                    "mMemoryCache.size() " + mMemoryCache.size());
            mMemoryCache.evictAll();
            Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
        }
        mMemoryCache = null;
    }
}

public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (mMemoryCache.get(key) == null) {
        if (key != null && bitmap != null)
            mMemoryCache.put(key, bitmap);
    } else
        Log.w(TAG, "the res is aready exits");
}

public synchronized Bitmap getBitmapFromMemCache(String key) {
    Bitmap bm = mMemoryCache.get(key);
    if (key != null) {
        return bm;
    }
    return null;
}

/**
 * 移除缓存
 * 
 * @param key
 */
public synchronized void removeImageCache(String key) {
    if (key != null) {
        if (mMemoryCache != null) {
            Bitmap bm = mMemoryCache.remove(key);
            if (bm != null)
                bm.recycle();
        }
    }
}

Le contenu ci-dessus est extrait de - technologie de mise en cache de mémoire LruCache pour la mise en cache d'images, référence logicielle


Résumé de cette section :

Cette section explique à tout le monde Les causes des problèmes de MOO sont également résumées sur Internet pour éviter le MOO causé par Bitmap. Certaines des solutions, étant donné que les applications créées par la société sont toutes basées sur des cartes et impliquent rarement des images, l'auteur n'a donc pas rencontré de problèmes de MOO. Je ne suis donc pas très familier avec cela ~ Dans le cours avancé suivant sur la gestion de la mémoire, nous allons lentement lutter contre ce problème de MOO. C'est tout pour cette section, merci~


Références : Analyse et solutions des problèmes de MOO dans les applications Android