加载没有OutOfMemoryError的大图像

我有一个5000 x 4000 px图像,我想画在canvas上。

首先,我尝试从资源加载它。 我把它放在/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"); 

在这种情况下, InputStream是一个FileInputStream ,在创建Drawable时我得到了一个OutOfMemoryError

所以我想知道:

有没有办法加载图像而不会出现错误? 或者有没有办法将FileInputStream转换为AssetInputStream

注意:

我不想调整图像大小,因为我正在实现缩放/平移function。 请不要告诉我阅读有效加载大位图 。

你可以在这里查看全class。 使用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 ,Galaxy S2没有抛出exception,但返回的Drawable为null。

所以我想目前唯一的解决方案是对图像进行下采样。

看一眼:

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

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

要么

 decodeFile (String pathName, BitmapFactory.Options opts) 

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

您需要设置正确的选项。

http://img.androidcookie.com/java/ppre codeBitmap 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); 

现在你有一组较小的图像。 如果你想完美地完成它,你可以回收这个位图:

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

从这一点开始,您可以将较小的图像放在canvas上。 在我的下一个代码中,我将图像放入ScrollView。 将图像放在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限制或其他),因此您必须始终将图像拆分为较小的部分。

我不确定这样做是否正确,但这仍然可以解决您的问题。

尝试在webview中打开图像。 这将为您提供内置的放大/缩小和平移function,您不必担心打开任何输入流或其他任何内容。

要从资产中打开webview中的图像:

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

您可以使用WebSettings来进行缩放控制。

希望对你有帮助!!

如果它从资源加载时工作,现在当你手动加载时它不起作用,那么我怀疑你在资源管理方面有一些问题,可能是一些内存泄漏。

OutOfMemoryError总是在应用程序启动时发生,还是在以后发生? 它是否在配置更改? 您是否使用静态字段(如果使用不正确,这通常会导致Android应用程序中的内存泄漏, 例如解释这适合您的情况)?
尝试使用一些分析工具( 谷歌可以帮助find有用的东西)。