【问题标题】:Android saving large bitmapsAndroid 保存大位图
【发布时间】:2012-08-28 09:33:02
【问题描述】:

我是android 的新手,我对如何处理Bitmaps 感到困惑。

我想下载一个Bitmap,它可能非常大,并将其保存到一个临时内部文件中。然后我将把这个Bitmap 画到Canvas 之后。

我目前的方法是 1.下载输入流 2.复制流 3. 使用bitmapFactory.options 使用一个流计算边界 4. 使用另一个流解码具有样本大小的完整位图

但是,我需要landscapeportrait 版本,所以现在我必须这样做两次并保存两张图像。

或者 - 我看到人们使用bm.compress(Bitmap.CompressFormat.JPEG, 50, bos); 来保存文件。这绕过了样本大小的解码,因为它直接从流中保存。我想当我绘制到我的Canvas 时,我会使用矩阵来缩放。

基本上,我对这项任务的最佳方法感到困惑,哪种方法不太可能出现内存不足并且是更常用的方法?

干杯

【问题讨论】:

    标签: android bitmap


    【解决方案1】:
      byte[] imagesByte = getLogoImage(Your url);
    

    设置为图像视图...

     imgView.setImageBitmap(BitmapFactory.decodeByteArray( imagesByte,  0, imagesByte.length));
    

    下载方法

     public static byte[] getLogoImage(String url){
                try {
                    URL imageUrl = new URL(url);
                    URLConnection ucon = imageUrl.openConnection();
    
                    InputStream is = ucon.getInputStream();
                    BufferedInputStream bis = new BufferedInputStream(is);
    
                    ByteArrayBuffer baf = new ByteArrayBuffer(500);
                    int current = 0;
                    while ((current = bis.read()) != -1) {
                        baf.append((byte) current);
                    }
    
                    return baf.toByteArray();
                } catch (Exception e) {
                    Log.d("ImageManager", "Error: " + e.toString());
                }
                return null;
            }
    

    【讨论】:

    • 我无法使用此方法,因为我想保存到内部存储中,我稍后也在画布上绘制 - 我没有图像视图。
    【解决方案2】:

    在 Android 中,您必须意识到内存有限,因此大图像将无法放入内存,并且您将遇到 OutOfMemory 异常。

    关键是,将图像保存到内部存储器后,以显示分辨率加载:

    首先下载图片,这应该在 UI 线程之外完成,让 _url 一个带有图片地址的 URL 实例和 _file 包含目标文件的字符串:

    URLConnection conn = _url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            boolean success = false; //track succesful operation
    
            if( _file != null)
            {
                try
                {
                    FileOutputStream fos = new FileOutputStream(_file);
                    byte data[] = new byte[4086]; //use 4086 bytes buffer
    
                    int count = 0;
                    while ((count = is.read(data)) != -1)
                    {
                        fos.write(data, 0, count);//write de data
                    }
    
                    is.close();
                    fos.flush();
                    fos.close();
    
                    int len =  conn.getContentLength();
                    File f = new File( _file);//check fie length is correct
                    if( len== f.length())
                    {
                        success = true;
                    }
                    else
                    {
                        //error downloading, delete de file
                        File tmp = new File( _file);
                        if( tmp.exists())
                        {
                            tmp.delete();
                        }
                    }
                }catch (Exception e )
                {
                    try
                    {
                        e.printStackTrace();
                        //delete file with errors
                        File tmp = new File( _file);
                        if( tmp.exists())
                        {
                            tmp.delete();
                        }
    
                    }
                    catch (Exception ex)
                    {
                        ex.printStackTrace();
                    }
                }
                finally
                {
                    is.close();//cleanup
    
                }
    

    然后当你必须以所需的分辨率加载图像时,这里的关键是使用 BitmapFactory 读取位图信息并获得缩放位图:

    public static Bitmap bitmapFromFile(int width, int height, String file)
    {
    
        Bitmap bitmap = null;
    
        final BitmapFactory.Options options = new BitmapFactory.Options();
    
        if( height >0 && width > 0 ) {
            options.inJustDecodeBounds = true;//only read bitmap metadata
            BitmapFactory.decodeFile(file,options);
    
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, width, height);
    
            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
    
        }
        try
        {
            bitmap = BitmapFactory.decodeFile(file, options);//decode scaled bitmap
        }catch (Throwable t)
        {
            if( bitmap != null)
            {
                bitmap.recycle();//cleanup memory, very important!
            }
            return null;
    
        }
        return bitmap
    }
    

    最后一步是计算比例因子:

    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;
            final int halfWidth = width;
    
            // 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 ((couldShrink(halfWidth, reqWidth, inSampleSize)&&
                    couldShrink(halfHeight,reqHeight, inSampleSize))
                    //&&(halfHeight*halfWidth)/inSampleSize > maxsize)
                    )
            {
                inSampleSize *= 2;
            }
    
        }
    
        return inSampleSize;
    }
    private static  boolean couldShrink ( int dimension, int req_dimension, int divider)
    {
        int actual = dimension / divider;
        int next = dimension / (divider*2);
    
        int next_error = Math.abs(next - req_dimension);
        int actual_error = Math.abs(actual-req_dimension);
    
        return next > req_dimension ||
                (actual > req_dimension && (next_error < actual_error) )
                ;
    }
    

    也就是说,如果您想手动完成,我建议您使用Picasso 来处理图像的下载、磁盘缓存和内存缓存:

    在下载时加载到名为image 的ImageView 中显示背景(R.drawable.img_bg):

    Picasso.with(image.getContext())
                .load(url).placeholder(R.drawable.img_bg).fit()
                        .into(image, new Callback.EmptyCallback()
                        {
                            @Override
                            public void onSuccess()
                            {
                                holder.progress.setVisibility(View.GONE); //hide progress bar
                            }
    
                            @Override
                            public void onError()
                            {
                                holder.progress.setVisibility(View.GONE); //hide progress bar
                                //do whatever you design to show error
                            }
                        });
    

    处理自己的位图:

    //first declare a target
    _target = new Target()
        {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
            {
                //handle your bitmap (store it and use it on you canvas
            }
    
            @Override
            public void onBitmapFailed(Drawable errorDrawable)
            {
                //handle your fail state
    
            }
    
            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable)
            {//for example for drawing a placeholder while downloading
            }
        };
    

    现在您只需要加载图像并调整其大小:

    Picasso.with(context).load(url).resize(192, 192).centerCrop().into(_target);
    

    希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-07
      • 2014-05-11
      • 2015-04-23
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多