`
yhz61010
  • 浏览: 561438 次
  • 来自: -
博客专栏
63c13ecc-ef01-31cf-984e-de461c7dfde8
libgdx 游戏开发
浏览量:12245
社区版块
存档分类
最新评论

[原创] 连载 1 - 深入讨论 Android 关于高效显示图片的问题 - 如何高效的加载大位图

阅读更多
  更加详细的说明,可以参阅如下官网地址:http://developer.android.com/training/building-graphics.html

  刚开始做 Android 应用时,以为显示图片是很简单的事,在模拟器里运行的好好的,一放到真机上,经常遇到类似于 java.lang.OutofMemoryError: bitmap size exceeds VM budget. 之类的异常。后来看了下官网的详细的介绍,才发现关于图片的显示说头还不少。
  下面就把学习心得与大家分享下:

  为什么显示图片会遇到很棘手的问题?
  手机显示一张 800 万像素的图片(现在主流的手机像素基本上都是 800 万像素以上),大约需要使用 32 MB 的内存,而这刚好是 Android 系统分配给每个应用的最大内存(有的 Android 设备分配给每个应用的最大内存只有 16 MB),所以如果手机应用直接打开这样一张图,基本上都会遇到由于内存溢出而导致程序被迫退出的情况。相信这种情况很多人可能都遇到过。

  以 Galaxy Nexus 为例,其后置相机的像素是 500 万,其分辨率为 2592x1936 像素。若位图设置使用的是 ARGB_8888 (在 Android 2.3 及更高版本中,该值为默认值),那么加载该图将占用大约 19 MB 的内存(2592*1936*4 bytes),因此程序很快就会耗尽 Android 分配给每个应用程序的最大内存,从而导致程序崩溃。

  即使应用程序不见得非要显示一张 500 万或更高像素的图片,如果程序设计不当,同样会在显示图片时,遇到程序崩溃的问题。例如,在图片相关的应用中,经常需要显示大量图片,因此经合会使用 ListView, GridView 或 ViewPager。若不对显示的图片进行处理,也会由于显示图片过多而导致程序崩溃。

  如何解决显示图片导致的内存溢出的问题?
  要解决这个问题,需要从以下五点入手:
  1. 如何高效的加载大位图。(如何解码大位图,避免超过每个应用允许使用的最大内存)http://yhz61010.iteye.com/blog/1848337
  2. 如何在非 UI 线程处理位图。(如何使用 AsyncTask 在后台线程处理位图及处理并发问题)http://yhz61010.iteye.com/blog/1848811
  3. 如何对位图进行缓存。(如何通过创建内存缓存和磁盘缓存来流畅的显示多张位图)http://yhz61010.iteye.com/blog/1849645
  4. 如何管理位图内存。(如何针对不同的 Android 版本管理位图内存)http://yhz61010.iteye.com/blog/1850232
  5. 如何在 UI 中显示位图。(如何通过 ViewPager 和 GridView 显示多张图片)http://yhz61010.iteye.com/blog/1852927

  下面我们来一一进行说明。

  如何高效的加载大位图?
  1. 获取位置尺寸及类型
  使用 BitmapFactory 对位图进行解码时,使用 BitmapFactory.Options。将 Options 的 inJustDecodeBounds 设置为 true 时,可以避免为位图分配内存,此时 BitmapFactory.decodeX 的返回结果为 null,但是会为 Options 设置 outWidth, outHeight 和 outMimeType 值。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

  通过上述代码,你就可以在不为位图分配内存的情况下,获得位图的宽,高及位图类型。之后在显示图片时,就可以通过获取的信息来判断是否需要对图片进行处理后再显示,从而避免内存溢出问题。

  2. 将缩小比例后的图片加载到内存
  若程序中仅仅是为了显示一张 128x96 大小的缩略图,而将一张原始大小为 1024x768 的图片加载到内存,就显得很不划算了。因此,对将要显示的图片进行等比例缩小后再进行显示就显得很有必要。

  通过设置 options.inSampleSize 来产生缩小后的图片。例如,若 options.inSampleSize = 4,那么对于一张原始大小为 2048x1536 的位图来说,产生的新位图大小约为 512x384。将这个新的位图加载到内存只需要 0.75 MB 内存,而原图则需要占用大约 12 MB 的内存。

  具体程序实现如下:
  首先,将 inJustDecodeBounds 设置为 true,获取位图信息,然后再设置新的 inSampleSize 值,最后再将 inJustDecodeBounds 设置为 false,从而将新生成的位图加载至内存。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

  上述方法中使用的 calculateInSampleSize 方法的实现如下:
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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}

  需要注意的是,上述方法返回的 inSampleSize 的值,最好是 2 的 n 次幂。(详见 http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize

  综上所述,有了上述这些方法,我们就可以在程序中加载任意大小的图片,而不用担心内存溢出的问题。例如,下述代码会将原始图片显示成 100x100 像素的缩略图:
mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
1
2
分享到:
评论
1 楼 ypppk 2013-07-02  
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeResource(getResources(), R.drawable.picture, options); //这里的位图是空的,原因是options.inJustDecodeBounds为true,但是可以获得位图的长宽,所以此次内存不会溢出

int orgHeight = options.outHeight; 
int orgWidth = options.outWidth; 
        options.inSampleSize = orgWidth/Static.screenWidth;  //按照比例缩放到屏幕宽度
        options.inJustDecodeBounds = false; 
        Static.picture = BitmapFactory.decodeResource(getResources(), R.drawable.picture, options); //绘制缩放后的位图

相关推荐

    GridView加载大量图片卡的问题

    在Android开发中,GridView是一种常见...综上所述,解决GridView加载大量图片卡顿的问题需要结合异步加载技术、高效的缓存策略以及合理的图片处理,通过这些方法,我们能够显著提升用户体验,打造流畅的图片加载效果。

    android 位图转单色位图

    总之,将Android的彩色位图转换为单色位图涉及到位图的加载、配置创建、像素转换以及保存或显示。这个过程需要对Android图形系统有深入的理解,并可能需要用到原生代码来实现1位深度的支持。通过这样的转换,开发者...

    Android从网络加载图片并显示在ImageView控件上

    在Android开发中,将网络上的图片加载到ImageView控件上是一项常见的需求,特别是在构建社交应用、电商应用或者新闻阅读类应用时。这个过程涉及到多个关键知识点,包括网络请求、图片缓存策略、线程管理以及UI更新等...

    mfc加载位图,并可以放大、缩小图片

    在MFC(Microsoft Foundation Classes)框架中,加载位图并实现图片的放大和缩小功能是一项常见的任务,尤其在开发图形用户界面(GUI)应用程序时。本文将深入探讨如何使用MFC来实现这一功能。 首先,我们需要了解...

    Android-使用Glide在Android中加载SVG

    Glide是一个流行的Android图片加载库,它以其高效、易用和强大的功能而闻名。本篇将详细讲解如何在Android应用中使用Glide加载SVG图像。 首先,我们需要了解SVG的基本概念。SVG是一种基于XML的图形语言,它可以描绘...

    笔记分享---Android 高清加载巨图

    本笔记将深入探讨如何高效地加载和管理高清大图,避免内存溢出(Out Of Memory,OOM)问题。 一、Bitmap对象与内存管理 1. Bitmap对象在内存中的存储:Android将Bitmap数据存储为一个像素数组,占用连续的内存空间...

    Android 大位图压缩方法二

    在Android开发中,大位图(Bitmap)的处理是一个重要的技术点,特别是在处理高分辨率图像或者需要显示大量图片的应用中。由于Android系统对内存管理的限制,直接加载大位图可能导致应用崩溃,这就是著名的"Out of ...

    VC-位图显示-根据位图大小自动调整窗口大小

    在VC++编程中,有时我们需要将位图显示在窗口上,并且希望窗口能根据位图的大小自动调整,以确保位图能完全显示。这个任务可以通过处理窗口消息和使用GDI(Graphics Device Interface)来实现。下面我们将深入探讨...

    glide加载和下载svg矢量图片

    在Android开发中,Glide是一个非常流行的图片加载库,它以其高效、易用和强大的功能深受开发者喜爱。SVG(Scalable Vector Graphics)是一种矢量图格式,与传统的位图图像不同,SVG图像可以无损缩放,不会因为放大而...

    窗口加载显示图片

    本文将深入探讨如何处理图片的压缩、显示以及解决颜色失真的问题。 首先,我们关注“窗口显示图片”这个主题。在各种操作系统中,无论是Windows、macOS还是Linux,窗口系统都提供了API或库函数来支持图像的加载和...

    从文件加载位图,并显示在窗口上

    在C++编程中,加载位图并将其显示在窗口上是一项基本任务,尤其在游戏开发中至关重要。位图(Bitmap)是一种常见的图像文件格式,它包含像素数据,用于表示图像。下面将详细介绍如何使用C++来实现这个过程,并提供...

    加载位图,基于MFC对话框

    位图是一种图像文件格式,通常用于显示静态图片。本篇文章将详细讲解如何在基于MFC对话框的应用程序中加载位图。 1. **位图资源的准备** 在开始编码之前,你需要有一个位图文件(如.bmp)。确保这个文件已经包含在...

    Android平台的图像控件,可以异步加载网络图片、项目资源和本地图片,并且支持双指缩放、图片的基本处理.zip

    在Android应用中,直接在主线程加载大图或网络图片可能导致应用无响应(ANR),因此异步加载成为必备功能。该控件支持在后台线程加载图片,避免阻塞UI,提高应用的响应速度。同时,它可能采用了诸如Picasso、Glide...

    android中的位图操作demo

    本示例“android中的位图操作demo”主要涵盖了Android应用开发中关于位图的基本操作,包括位图的加载、绘制以及一些高级功能,如创建倒影效果。以下是对这些知识点的详细说明: 1. **位图的加载**:在Android中,...

    10-android ImageView 图片视图

    在Android开发中,ImageView是用于显示图像资源的重要组件。它能够加载本地图片或者网络图片,是用户界面设计中不可或缺的一部分。本篇文章将深入探讨ImageView的工作原理、如何使用以及相关的优化技巧。 1. **...

    android-image-filter-ndk,使用android ndk在c中处理位图的android示例项目.zip

    总结,"android-image-filter-ndk"项目为我们提供了一个实战范例,展示了如何结合Android NDK和C语言实现高效的图像处理。通过学习该项目,开发者不仅可以掌握Android NDK的使用,还能深入理解图像处理的原理和技巧...

    android大位图二次采样处理三

    1. 内存限制:Android系统对每个应用分配了一定量的内存,当加载大位图时,如果超过分配的内存限制,可能会引发OutOfMemoryError。 2. 图片解码:大位图在内存中解码时会占用大量空间,即使图片在磁盘上的大小并不大...

    Android应用源码之Android 图片缓存、加载器.zip

    本资源"Android应用源码之Android 图片缓存、加载器.zip"提供了一个实际的安卓实例,旨在帮助开发者理解和实现高效的图片处理机制。以下将详细阐述相关知识点。 1. **图片加载库的选择与原理** - Android开发中...

    android应用源码图片放大缩小旋转涂鸦源码.zip

    1. **图片加载库**:在Android中,高效地加载和显示图片是必不可少的。源码可能使用了如Glide、Picasso或 Fresco等流行的图片加载库,它们能优化内存使用,避免内存溢出,并提供缓存机制。 2. **手势识别**:为了...

    Android图片裁剪----移动、缩放图片进行裁剪

    在实际开发中,我们可能会遇到性能问题,特别是处理大尺寸图片时。为了避免内存溢出,可以考虑使用Bitmap的inSampleSize来降低图片加载的分辨率,或者使用BitmapRegionDecoder来按需解码部分图像。 JImageEditDemo...

Global site tag (gtag.js) - Google Analytics