código simple androide para lograr la compresión de la imagen final no es oom

Recientemente me encontré con un hoyo dejado por sus predecesores, el siguiente 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)

Problema Causa:

ListView cepillo imágenes se toman cada vez que el cuadro local y hacer una serie de operaciones tales como la compresión, pero la compresión no es buena causa después de la compresión para completar el cuadro sigue siendo muy grande, por encima ocurrió Crash.

Ahora he publicado sobre el tema del código:

El código tiene los siguientes problemas:

1. Foto no caché de cada carga para calcular la compresión

2. hilo abierto directo, el grupo de subprocesos no se usa

3. El método de compresión para escribir muertos, el cálculo de contracción relación en cuestión

No es el tamaño de la compresión de imágenes 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;
        }

Frente a estos problemas, en combinación con la herramienta en línea que los datos tenía una clase:

Para la pregunta anterior es necesario lo siguiente:

1. caché

2. Local carga la imagen de red

3. Imágenes de compresión extrema

4. grupo de subprocesos de gestión multi-tarea

El cambio de subproceso asíncrono de actualización de Vista

En primer lugar, la memoria caché

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

, La segunda herramienta de compresión de imágenes

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

En tercer lugar, las herramientas de carga de imágenes

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

}

 

Publicado 92 artículos originales · ganado elogios 27 · Vistas a 90000 +

Supongo que te gusta

Origin blog.csdn.net/zhuxingchong/article/details/101071091
Recomendado
Clasificación