Android simple code to achieve the ultimate picture compression is not oom

Recently I encountered a pit left by their predecessors, the following error.

 java.lang.RuntimeException: Canvas: trying to draw too large(268435456bytes) bitmap.
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:229)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:98)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:545)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.widget.ImageView.onDraw(ImageView.java:1360)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.View.draw(View.java:20211)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.View.updateDisplayListIfDirty(View.java:19086)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.View.draw(View.java:19939)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.View.draw(View.java:20214)
09-20 17:06:39.298 26126 26126 E AndroidRuntime:        at android.view.View.updateDisplayListIfDirty(View.java:19086)

problem causes:

ListView brush pictures are taken each time the local picture and do a series of operations such as compression, but compression is not good cause after compression to complete the picture is still very large, above Crash occurred.

Now I posted about the issue of code:

The code has the following problems:

1. Picture no cache each load to be calculated compression

2. Direct open thread, the thread pool is not used

3. The compression method to write dead, shrink-ratio calculation in question

Not the size of the picture compression 4. Fangsuo

private void loadBitmapsTask(String path,ImageView imageView){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = getImage(path);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if(imageView != null && bitmap !=null){
                            ImageView imageViewImage = imageView;
                            if(imageViewImage != null){
                                imageViewImage.setImageBitmap(bitmap);
                            }
                        }
                    }
                });
            }
        }).start();
    }


private static Bitmap getImage(String srcPath){
            BitmapFactory.Options newopts = new BitmapFactory.Options();
            //返回bitmap尺寸
            newopts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(srcPath,newopts);
            //获取bitmap宽高
            int w = newopts.outWidth;
            int h = newopts.outHeight;
            float resolutionH = 80f;
            float resolutionW = 80f;
            int be = 1;
            if(w > h && w>resolutionW){
                be = (int)(w/resolutionW);
            }else if(w < h && h > resolutionH){
                be = (int)(h/resolutionH);
            }
            if(be <= 0 ){
                be = 1;
            }
            newopts.inJustDecodeBounds = false;
            newopts.inSampleSize = be;
            Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newopts);
            return bitmap;
        }

The face of these problems, combined with online data tool I had a class:

For the above question we need the following:

1. Cache

2. Local network image loading

3. Pictures extreme compression

4. multi-task management thread pool

The thread switch asynchronous refresh View

First, the memory cache

public class ImageCache extends LruCache<String, Object> {

    private Map<String, SoftReference<Object>> cacheMap;

    public ImageCache(Map<String, SoftReference<Object>> cacheMap) {
        super((int) (Runtime.getRuntime().maxMemory() / 8));
        this.cacheMap = cacheMap;
    }

    @Override
    protected int sizeOf(String key, Object value) {
        if(value instanceof Bitmap){
            return ((Bitmap)value).getRowBytes() * ((Bitmap)value).getHeight();
        }else{
            return super.sizeOf( key,  value);
        }
    }

    @Override
    protected void entryRemoved(boolean evicted, String key, Object oldValue, Object newValue) {
        if (oldValue != null) {
            SoftReference<Object> softReference = new SoftReference<>(oldValue);
            cacheMap.put(key, softReference);
        }
    }

    public Map<String, SoftReference<Object>> getCacheMap() {
        return cacheMap;
    }
}

Second, the picture compression tool

public class ImageCompressUtils {
    //图片最后压缩大小访问小于50kb
    private static final int IMAGE_COMPRESS_MAXS_IZE = 1024 * 50;

    /**
     *
     * @param srcPath
     * @param width
     * @param height
     * @return
     */
    public static Bitmap getImageFromLocal(String srcPath, int width, int height) {
        BitmapFactory.Options newopts = new BitmapFactory.Options();
        //返回bitmap尺寸
        newopts.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(srcPath, newopts);
        newopts.inJustDecodeBounds = false;
        newopts.inSampleSize = calculateInSampleSize(newopts, width, height);
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newopts);
        return compressImage(bitmap);
    }

    /**
     * 计算图片放缩到目标大小
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    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;
    }
    
    /**
     * 质量压缩方法
     * @param image
     * @return
     */
    public static Bitmap compressImage(Bitmap image) {
        if (image == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        int options = 80;
        // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
        while (baos.toByteArray().length > IMAGE_COMPRESS_MAXS_IZE && options >= 0) {
            // 重置baos即清空baos
            baos.reset();
            // 这里压缩options%,把压缩后的数据存放到baos中
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);
            // 每次都减少5
            options -= 5;
        }
        // 把压缩后的数据baos存放到ByteArrayInputStream中
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        // 把ByteArrayInputStream数据生成图片
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
        return bitmap;
    }
}

Third, the image loading tools

public class ImageUtils {
    //网络下载缓存路径
    private static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Picture";
    //图片缓存
    private static ImageCache mCaches;
    private static ImageUtils mInstance;

    private static Handler mHandler;

    //声明线程池,全局只有一个线程池,所有访问网络图片,只有这个池子去访问。
    private static ExecutorService mPool;

    private Map<ImageView, String> mTags = new LinkedHashMap<>();

    private ImageUtils() {
        Map<String, SoftReference<Object>> cacheMap = new HashMap<>();
        mCaches = new ImageCache(cacheMap);
    }

    public static ImageUtils getInstance() {
        if (mInstance == null) {
            synchronized (ImageUtils.class) {
                if (mInstance == null) {
                    mInstance = new ImageUtils();
                    if (mHandler == null) {
                        //实例化Handler
                        mHandler = new Handler(Looper.getMainLooper());
                    }

                    if (mPool == null) {
                        mPool = Executors.newCachedThreadPool();
                    }
                }
            }
        }
        return mInstance;
    }

    /**
     * 给imageView加载url对应的图片
     * @param iv
     * @param url
     */
    public void display(ImageView iv, String url) {
        //1.从内存中获取
        Bitmap bitmap = getBitmapFromMemory(url);
        if (bitmap != null) {
            //内存中有,显示图片
            iv.setImageBitmap(bitmap);
            return;
        }
        //2.内存中没有,从本地获取
        bitmap = loadFromLocal(url);
        if (bitmap != null) {
            //本地有,显示
            iv.setImageBitmap(bitmap);
            return;
        }

        //从网络中获取
        loadFromNet(iv, url);
    }

    /**
     * 加载图片
     * @param iv
     * @param url
     */
    public void loadFromLocal(ImageView iv, String url) {
        mTags.put(iv, url);//url是ImageView最新的地址
        //1.从内存中获取
        Bitmap bitmap = getBitmapFromMemory(url);
        if (bitmap != null) {
            //内存中有,显示图片
            iv.setImageBitmap(bitmap);
            return;
        }
        //用线程池去管理
        mPool.execute(new LocalImageTask(iv, url));
    }

    private void loadFromNet(ImageView iv, String url) {
        mTags.put(iv, url);//url是ImageView最新的地址
        //用线程池去管理
        mPool.execute(new NetImageTask(iv, url));
    }

    /**
     * 本地加载图片任务,包括加载防缩,压缩等过程
     */
    private class LocalImageTask implements Runnable {
        private ImageView iv;
        private String url;

        public LocalImageTask(ImageView iv, String url) {
            this.iv = iv;
            this.url = url;
        }

        @Override
        public void run() {
            //2.内存中没有,从本地获取
            Bitmap bitmap = loadFromLocal(url);
            //存储到内存
            mCaches.put(url, bitmap);

            //在显示UI之前,拿到最新的url地址
            String recentlyUrl = mTags.get(iv);

            //把这个url和最新的url地址做一个比对,如果相同,就显示ui
            if (url.equals(recentlyUrl)) {
                //显示到UI,当前是子线程,需要使用Handler。其中post方法是执行在主线程的
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        display(iv, url);
                    }
                });
            }
        }
    }

    /**
     * 网络加载任务
     */
    private class NetImageTask implements Runnable {
        private ImageView iv;
        private String url;

        public NetImageTask(ImageView iv, String url) {
            this.iv = iv;
            this.url = url;
        }

        @Override
        public void run() {
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

                //连接服务器超时时间
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                //连接服务器(可写可不写)
                conn.connect();

                //获取流
                InputStream is = conn.getInputStream();

                //将流变成bitmap
                Bitmap bitmap = BitmapFactory.decodeStream(is);

                //存储到本地
                save2Local(bitmap, url);

                //存储到内存
                mCaches.put(url, bitmap);

                //在显示UI之前,拿到最新的url地址
                String recentlyUrl = mTags.get(iv);

                //把这个url和最新的url地址做一个比对,如果相同,就显示ui
                if (url.equals(recentlyUrl)) {
                    //显示到UI,当前是子线程,需要使用Handler。其中post方法是执行在主线程的
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            display(iv, url);
                        }
                    });
                }


            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 存储到本地
     *
     * @param bitmap
     * @param url
     */
    public void save2Local(Bitmap bitmap, String url) throws FileNotFoundException {
        File file = getCacheFile(url);
        FileOutputStream fos = new FileOutputStream(file);
        /**
         * 用来压缩图片大小
         * Bitmap.CompressFormat format 图像的压缩格式;
         * int quality 图像压缩率,0-100。 0 压缩100%,100意味着不压缩;
         * OutputStream stream 写入压缩数据的输出流;
         * 返回值:如果成功地把压缩数据写入输出流,则返回true。
         */
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
    }

    /**
     * 从本地获取图片
     *
     * @param url
     * @return bitmap
     */
    private Bitmap loadFromLocal(String url) {
        //本地需要存储路径
        File file = new File(url);

        if (file.exists()) {
            //本地有
            //把文件解析成Bitmap
            Bitmap bitmap = ImageCompressUtils.getImageFromLocal(url, 80, 80);
            //存储到内存
            setBitmapToMemory(url, bitmap);
            mCaches.put(url, bitmap);

            return bitmap;
        }

        return null;
    }


    /**
     * 获取缓存文件路径(缓存目录)
     *
     * @return 缓存的文件
     */
    private File getCacheFile(String url) {
        String name = url;

        File dir = new File(CACHE_PATH);
        if (!dir.exists()) {
            //文件不存在,就创建
            dir.mkdirs();
        }
        //此处的url可能会很长,一般会使用md5加密
        return new File(dir, name);
    }

    /**
     * 从内存中读图片
     * @param url
     */
    public Bitmap getBitmapFromMemory(String url) {
        Bitmap bitmap = (Bitmap) mCaches.get(url);
        // 如果图片不存在强引用中,则去软引用(SoftReference)中查找
        if (bitmap == null) {
            Map<String, SoftReference<Object>> cacheMap = mCaches.getCacheMap();
            SoftReference<Object> softReference = cacheMap.get(url);
            if (softReference != null) {
                bitmap = (Bitmap) softReference.get();
                //重新放入强引用缓存中
                mCaches.put(url, bitmap);
            }
        }
        return bitmap;

    }

    /**
     * 往内存中写图片
     * @param url
     * @param bitmap
     */
    public void setBitmapToMemory(String url, Bitmap bitmap) {
        mCaches.put(url, bitmap);
    }

    public void clear() {
        mCaches.getCacheMap().clear();
    }

}

 

Published 92 original articles · won praise 27 · views 90000 +

Guess you like

Origin blog.csdn.net/zhuxingchong/article/details/101071091