装载大图像,而不会OutOfMemoryError异常(Loading large images w

2019-06-27 00:30发布

我有我要画到画布上一个5000 X 4000像素的图片。

首先,我试图从资源加载它。 我把它放在/res/drawable

我用下面的方法:

InputStream input = getResources().openRawResource(R.drawable.huge_image);
Drawable d = Drawable.createFromStream(input, "image");
d.setBounds(...);
d.draw(canvas);

它的工作就像一个魅力。

在这种情况下InputStreamAssetManager.AssetInputStream

所以现在我想从SD卡加载它。

这里就是我试图做的:

File f = new File(path);
Uri uri = Uri.fromFile(f);
InputStream input = mContext.getContentResolver().openInputStream(uri);
Drawable d = Drawable.createFromStream(input, "image");

在这种情况下InputStreamFileInputStream ,我得到一个OutOfMemoryError创建时Drawable

所以我想知道:

有没有一种方法来加载图像没有得到这个错误? 还是有办法一个转换FileInputStreamAssetInputStream

注意:

我不想调整图片的大小,因为我实现缩放/平移功能。 请不要告诉我读高效加载大型位图 。

您可以检查出完整的类在这里 。 使用时,会发生错误setImageUri()

这是我的错误日志:

08-13 11:57:54.180: E/AndroidRuntime(23763): FATAL EXCEPTION: main
08-13 11:57:54.180: E/AndroidRuntime(23763): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:468)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:332)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.ZoomImageView.setDrawablefromUri(ZoomImageView.java:187)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.ZoomImageView.setImageUri(ZoomImageView.java:588)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.TestActivity.onKeyDown(TestActivity.java:30)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.KeyEvent.dispatch(KeyEvent.java:1257)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.app.Activity.dispatchKeyEvent(Activity.java:2075)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1673)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2493)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2463)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1752)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.os.Looper.loop(Looper.java:144)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.app.ActivityThread.main(ActivityThread.java:4937)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at java.lang.reflect.Method.invokeNative(Native Method)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at java.lang.reflect.Method.invoke(Method.java:521)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at dalvik.system.NativeStart.main(Native Method)

编辑:

我上的HTC Desire A8181测试我的代码。 被告知,第一代码片段是不工作的其他一些设备后,我在三星Galaxy S2和在模拟器上进行测试。

结果:从资源加载,仿真器给了一个OutOfMemoryError ,银河S2并没有抛出异常,但返回的Drawable为null。

所以我想暂时唯一的解决办法是下采样图像。

Answer 1:

看一眼:

http://developer.android.com/reference/android/graphics/BitmapFactory.html

decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)

要么

decodeFile (String pathName, BitmapFactory.Options opts)

这样你可以加载你的整个的位图,并显示在屏幕上。

您需要设置正确的选项。

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

inSampleSize

我与你的代码试了一下:

 BitmapFactory.Options options = new BitmapFactory.Options();

 options.inSampleSize = 4;

 Bitmap myBitmap = BitmapFactory.decodeFile(mUri.getPath(),options);
 Drawable d = new BitmapDrawable(Resources.getSystem(),myBitmap);
 updateDrawable(d);

我的作品。



Answer 2:

在我看来,最好的办法是将图像分割成更小的碎片。 这将阻止应用程序可以给一个OutOfMemoryException。 这里是我的示例代码

首先从SD卡获取图像的把它变成一个位图

Bitmap b = null;
        try {
            File sd = new File(Environment.getExternalStorageDirectory() + link_to_image.jpg);
            FileInputStream fis;

            fis = new FileInputStream(sd);

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPurgeable = true;
            options.inScaled = false;

            b = BitmapFactory.decodeStream(fis, null, options);
            fis.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

分裂位图(b)转化为更小的碎片。 在这种情况下SPLIT_HEIGHT为400。

double rest = (image_height + SPLIT_HEIGHT);
        int i = 0;
        Bitmap[] imgs = new Bitmap[50];

        do {
            rest -= SPLIT_HEIGHT;
            if (rest < 0) {
                // Do nothing
            } else if (rest < SPLIT_HEIGHT) {
                imgs[i] = Bitmap.createBitmap(b, 0, (i * SPLIT_HEIGHT), image_width, (int) rest);
            } else {
                imgs[i] = Bitmap.createBitmap(b, 0, i * SPLIT_HEIGHT, image_width, SPLIT_HEIGHT);
            }

            i++;

        } while (rest > SPLIT_HEIGHT);

现在你有更小的图像集合。 如果你想perfecly做到这一点,你可以回收此位图:

// Not needed anymore, free up some memory
        if (b != null)
            b.recycle();

从这一点就可以把较小的图像到画布上。 在我的下一个代码,我把图像转换成一个滚动型。 为把图像在Canvas的代码,我认为差别不大。

// Add images to scrollview
        for (int j = 0; j < imgs.length; j++) {
            Bitmap tmp = imgs[j];
            if (tmp != null) {
                // Add to scrollview
                ImageView iv = new ImageView(activity);

                String id = j + "" + articlenumber;
                iv.setId(Integer.parseInt(id));
                iv.setTag(i);

                iv.setImageBitmap(tmp);

                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(globalwidth, tmp.getHeight());
                lp.addRule(RelativeLayout.BELOW, previousLongPageImageId);
                lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);

                container.addView(iv, lp);
                previousLongPageImageId = iv.getId();
            }
        }

我希望这能帮到您。

注:图片的最大分辨率为2048 * 2048(OpenGL的限制或东西),所以你将永远有图像分割成更小的碎片。



Answer 3:

我不知道它是否是正确的这样做,但还是这样可以解决你的问题的方式。

尝试在网页视图打开你的形象。 这将为您提供内置的放大/缩小和平移功能,你不要担心打开任何输入流或其他任何东西。

为了从资产的WebView打开一幅图像:

    WebView  wv = (WebView) findViewById(R.id.webView1);  
    wv.loadUrl("file:///android_asset/abc.png");  

您可以WebSettings允许缩放控件。

希望对你有帮助!!



Answer 4:

如果它的工作,当你从资源加载,现在当你手动加载,然后我怀疑你有一些问题,资源管理,可能是一些内存泄漏这是行不通的。

是否OutOfMemoryError总是发生在应用程序启动时或在它以后会发生什么? 它是对配置变化? 你使用静态字段(如果使用不当,这经常会导致内存泄漏的Android应用程序, 例如与解释这符合你的情况)?
尽量使用一些分析工具( 谷歌可以帮助找到一些有用的东西)。



文章来源: Loading large images without OutOfMemoryError