关于android 图片加载压缩处理


android应用对图片处理算是比较频繁的了,尤其是在程序加载大量图片和高分辨率图片时,最容易产生oom异常,下面是个人平时一些省内存加载方法

 

resoursce加载

public Bitmap ReadBitMap(Context context, int resId){    
        BitmapFactory.Options opt = new BitmapFactory.Options();    
        opt.inPreferredConfig = Bitmap.Config.RGB_565;   //小于ARGB_8888  样式大小
        opt.inPurgeable = true;    
        opt.inInputShareable = true;    
        //获取资源图片    
        InputStream is = context.getResources().openRawResource(resId);    
        return BitmapFactory.decodeStream(is,null,opt);    
  }   

 

 计算bitmap内存大小:

int calculateBitmapSize(Bitmap candidate,Resources res, int resId){
		BitmapFactory.Options targetOptions=new BitmapFactory.Options();
		targetOptions.inJustDecodeBounds = true;
		BitmapFactory.decodeResource(res, resId, targetOptions);
		int width = targetOptions.outWidth / targetOptions.inSampleSize;
        int height = targetOptions.outHeight / targetOptions.inSampleSize;
        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
        return byteCount;
	}
	/**
	 * A helper function to return the byte usage per pixel of a bitmap based on its configuration.
	 */
	static int getBytesPerPixel(Config config) {
	    if (config == Config.ARGB_8888) {
	        return 4;
	    } else if (config == Config.RGB_565) {
	        return 2;
	    } else if (config == Config.ARGB_4444) {
	        return 2;
	    } else if (config == Config.ALPHA_8) {
	        return 1;
	    }
	    return 1;
	}

 

指定尺寸压缩:

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);
}

 

如果你的控件大小小于原始图片大小,那么就需要对图片进行压缩处理,来减少内存使用。

现在知道了原图片的尺寸,根据实际情况决定你要加载它缩小多少倍后的图片。例如你用一个128x96的ImageView显示一张1024x768的原图,根本没有必要把原图读加载到内存。
加载一张缩小后的图片到内存,只需要把BitmapFactory.Options对象的inSampleSize设为true,
然后给inSampleSize设一个值就行了(可以理解inSampleSize为n,图片就缩小到1/n大小)。

 

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);  
}  
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) {  
        if (width > height) {  
            inSampleSize = Math.round((float)height / (float)reqHeight);  
        } else {  
            inSampleSize = Math.round((float)width / (float)reqWidth);  
        }  
    }  
    return inSampleSize;  
}  

 

 

官方压缩计算:更新2016-07-16

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);
}

 
2017-5-26更新参考picasso计算图形拉伸处理

 static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options,
                                      boolean centerInside) {
        calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options,
                centerInside);
    }

    static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
                                      BitmapFactory.Options options, boolean centerInside) {
        int sampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int heightRatio;
            final int widthRatio;
            if (reqHeight == 0) {
                sampleSize = (int) Math.floor((float) width / (float) reqWidth);
            } else if (reqWidth == 0) {
                sampleSize = (int) Math.floor((float) height / (float) reqHeight);
            } else {
                heightRatio = (int) Math.floor((float) height / (float) reqHeight);
                widthRatio = (int) Math.floor((float) width / (float) reqWidth);
                sampleSize = centerInside
                        ? Math.max(heightRatio, widthRatio)
                        : Math.min(heightRatio, widthRatio);
            }
        }
        options.inSampleSize = sampleSize;
        options.inJustDecodeBounds = false;
    }

 use:

void useCalucateBitmapCompress(){
        if (calculateSize) {
            BitmapFactory.decodeStream(stream, null, options);
            calculateInSampleSize(request.targetWidth, request.targetHeight, options,
                    true);
        }
        Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
        if (bitmap == null) {
            // Treat null as an IO exception, we will eventually retry.
            throw new IOException("Failed to decode stream.");
        }
        return bitmap;
    }

 

使用内存缓存

对于缓存,没有大小或者规则适用于所有应用,它依赖于你分析自己应用的内存使用确定自己的方案。
缓存太小可能只会增加额外的内存使用,缓存太大可能会导致内存溢出或者应用其它模块可使用内存太小

 

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

 

使用磁盘缓存

你的应用也有可能被其他任务打断,如电话呼入,应用在后台有可能会被结束,这样缓存的数据也会丢失。
当用户回到应用时,所有的图片还需要重新获取一遍。
磁盘缓存可应用到这种场景中,它可以减少你获取图片的次数,当然,从磁盘获取图片比从内存中获取要慢的多,所以它需要在非UI线程中完成。
示例代码中是磁盘缓存的一个实现,在Android4.0源码中(libcore/luni/src/main/java/libcore/io/DiskLruCache.java),
有更加强大和推荐的一个实现,它的向后兼容使在已发布过的库中很方便使用它

private DiskLruCache mDiskCache;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Initialize memory cache
    ...
    File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
    mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
    ...
}

class BitmapWorkerTask extends AsyncTask {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final String imageKey = String.valueOf(params[0]);

        // Check disk cache in background thread
        Bitmap bitmap = getBitmapFromDiskCache(imageKey);

        if (bitmap == null) { // Not found in disk cache
            // Process as normal
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100));
        }

        // Add final bitmap to caches
        addBitmapToCache(String.valueOf(imageKey, bitmap);

        return bitmap;
    }
    ...
}

public void addBitmapToCache(String key, Bitmap bitmap) {
    // Add to memory cache as before
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }

    // Also add to disk cache
    if (!mDiskCache.containsKey(key)) {
        mDiskCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromDiskCache(String key) {
    return mDiskCache.get(key);
}

// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getCacheDir(Context context, String uniqueName) {
    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
    // otherwise use internal cache dir
    final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
            || !Environment.isExternalStorageRemovable() ?
                    context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();

    return new File(cachePath + File.separator + uniqueName);
}

 

2019-10-14,相机图片压缩并旋转

    private fun getBitmap(uri: Uri): Bitmap? {
        var ism: InputStream? = null
        var returnedBitmap: Bitmap? = null
        try {
//            ism = mContentResolver!!.openInputStream(uri)
            val f = File(uri.path)
            if(false==f.exists()){
                return null
            }
            ism = FileInputStream(f)
            //Decode image size
            val o = BitmapFactory.Options()
            o.inJustDecodeBounds = true
            BitmapFactory.decodeStream(ism, null, o)
            ism!!.close()
//            BitmapFactory.decodeFile(f.path, o)
            var scale = 1
            if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                scale = Math.pow(2.0, Math.round(Math.log(IMAGE_MAX_SIZE / Math.max(o.outHeight, o.outWidth).toDouble()) / Math.log(0.5)).toInt().toDouble()).toInt()
            }
            o.inSampleSize = scale
            o.inJustDecodeBounds = false
//            val o2 = BitmapFactory.Options()
//            o2.inSampleSize = scale
//            o2.inJustDecodeBounds = false
//            ism = mContentResolver!!.openInputStream(uri)

//            val inputStream = FileInputStream(f)
//            val bytes = readStream(inputStream)
//            val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size,o2)
//            val bitmap = BitmapFactory.decodeStream(inputStream, null, o2)
            val loadBitmap = BitmapFactory.decodeFile(f.path, o)
//            ism!!.close()
            returnedBitmap = fixOrientationBugOfProcessedBitmap(bitmap)
            return returnedBitmap
//            return loadBitmap
        } catch (e: FileNotFoundException) {
            Log.d(TAG, "FileNotFoundException")
        } catch (e: IOException) {
            Log.d(TAG, "IOException")
        }

        return null
    }

    private fun fixOrientationBugOfProcessedBitmap(bitmap: Bitmap?): Bitmap? {
        try {
            if (getCameraPhotoOrientation(this, Uri.parse(mFileTemp!!.path)) == 0) {
                return bitmap
            } else {
                val matrix = Matrix()
                matrix.postRotate(getCameraPhotoOrientation(this, Uri.parse(mFileTemp!!.path)).toFloat())
                // recreate the new Bitmap and set it back
                return Bitmap.createBitmap(bitmap!!, 0, 0, bitmap.width, bitmap.height, matrix, true)
            }
        } catch (ex: Exception) {
            ex.printStackTrace()
            return null
        }

    }

 

 


 

 





 

posted @ 2013-07-14 14:25  HappyCode002  阅读(188)  评论(0编辑  收藏  举报