由于位图尺寸过大而导致内存不足异常

3

由于虚拟内存堆大小有限,我遇到了内存不足的问题。
这是我从服务器获取位图的代码:

@SuppressWarnings("unchecked")
public class DrawableManager {

@SuppressWarnings("rawtypes")
private final Map drawableMap;

    @SuppressWarnings("rawtypes")
    private DrawableManager() {
        drawableMap = new HashMap();
    }

    static private DrawableManager  _instance;

    static public DrawableManager getInstance() {
        if(_instance == null) {
            _instance = new DrawableManager();
        }
        return _instance;
    }

    public Bitmap fetchBitmap(final String sURL) {
        if(sURL.length() == 0)
            return null;
        Bitmap bm = (Bitmap) drawableMap.get(sURL);
        if(bm != null) {
            return bm;
        }

        byte[] imageData = ThumbImg(sURL);

        if(imageData == null)
            return null;

        if(imageData.length > 0) {
            bm =  BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            if(bm != null) {
                drawableMap.put(sURL, bm);
            }
            return bm;
        }
        else { 
            return null;
        }
    }

    public void fetchBitmapOnThread(final String sURL, final ImageView imageView) {
        if (drawableMap.containsKey(sURL)) {
            imageView.setImageBitmap((Bitmap) drawableMap.get(sURL));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageBitmap((Bitmap) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = fetchBitmap(sURL);
                Message message = handler.obtainMessage(1, bitmap);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }


    @SuppressWarnings("unused")
    public static byte[] ThumbImg(String imgUrl) 
    {

        //first check in the cache, if not available then store in the sd card memory
            HttpURLConnection connection = null;
            String userAgent = null;

            try
            {
                URL url = new URL(imgUrl);
                connection = ( HttpURLConnection ) url.openConnection();
                 if(userAgent != null) {
                     connection.setRequestProperty("User-Agent", userAgent);
                 }
                 connection.setConnectTimeout(5000);
                 connection.setReadTimeout(5000);
                    int CHUNKSIZE = 8192;        //size of fixed chunks
                int BUFFERSIZE = 1024;       //size of reading buffer


                 int bytesRead = 0;
                 byte[] buffer = new byte[BUFFERSIZE];   //initialize buffer
                 byte[] fixedChunk = new byte[CHUNKSIZE]; //initialize 1st chunk
                 ArrayList<byte[]> BufferChunkList = new ArrayList<byte[]>(); // List of chunk data
                 int spaceLeft = CHUNKSIZE;
                 int chunkIndex = 0;

                 DataInputStream in = new DataInputStream(connection.getInputStream() );

                 while( ( bytesRead = in.read( buffer ) ) != -1 ) { //loop until the DataInputStream is completed
                     if(bytesRead > spaceLeft) {
                         //copy to end of current chunk
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, spaceLeft);
                         BufferChunkList.add(fixedChunk);

                         //create a new chunk, and fill in the leftover
                         fixedChunk = new byte[CHUNKSIZE];
                         chunkIndex = bytesRead - spaceLeft;
                         System.arraycopy(buffer, spaceLeft, fixedChunk, 0, chunkIndex);
                     } else {
                         //plenty of space, just copy it in
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, bytesRead);
                         chunkIndex = chunkIndex + bytesRead;
                     }
                     spaceLeft = CHUNKSIZE - chunkIndex;
                 }

                 if (in != null) {
                     in.close();
                 }

                 // copy it all into one big array
                 int responseSize = (BufferChunkList.size() * CHUNKSIZE) + chunkIndex;  
                 Log.d("response size",""+responseSize);
                 byte[] responseBody = new byte[responseSize];
                 int index = 0;
                 for(byte[] b : BufferChunkList) {
                     System.arraycopy(b, 0, responseBody, index, CHUNKSIZE);
                     index = index + CHUNKSIZE;
                 }
                 System.arraycopy(fixedChunk, 0, responseBody, index, chunkIndex);

                return responseBody;                     


            }catch(SocketTimeoutException se)
            {

            }
            catch(Exception e)
            {

                e.printStackTrace();
            }finally
            {
                if(connection!=null)
                connection.disconnect();
            }

        return null;
    }

}

这是我使用的代码,对于较小的图像可以正常工作,但对于大图像则存在问题。问题在哪里并如何解决?
谢谢。

http://codingaffairs.blogspot.com/2016/07/processing-bitmap-and-memory-management.html - Developine
2个回答

6

将位图存储在内存中并不总是一个好主意。如果您真的想这样做,请尝试使用SoftReference来存储您的位图。查看此处

将地图的值参数设置为SoftReference<Bitmap>。然后在搜索地图时,使用此代码片段

@SuppressWarnings("unchecked")
public class DrawableManager {

@SuppressWarnings("rawtypes")
private final Map<String, SoftReference<Bitmap>> drawableMap;

    @SuppressWarnings("rawtypes")
    private DrawableManager() {
        drawableMap = new HashMap<String, SoftReference<Bitmap>>();
    }

    static private DrawableManager  _instance;

    static public DrawableManager getInstance() {
        if(_instance == null) {
            _instance = new DrawableManager();
        }
        return _instance;
    }

    public Bitmap fetchBitmap(final String sURL) {
        if(sURL.length() == 0)
            return null;
        Bitmap bm = null;
            SoftReference<Bitmap> reference = drawbaleM.get(imagePath);                  
            if(reference != null) bm = reference.get();
            if(bm != null) {
             return bm;
            }

        byte[] imageData = ThumbImg(sURL);

        if(imageData == null)
            return null;

        if(imageData.length > 0) {
            bm =  BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            if(bm != null) {
                drawableMap.put(sURL, bm);
            }
            return bm;
        }
        else { 
            return null;
        }
    }

    public void fetchBitmapOnThread(final String sURL, final ImageView imageView) {
        if (drawableMap.containsKey(sURL)) {
            imageView.setImageBitmap((Bitmap) drawableMap.get(sURL));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageBitmap((Bitmap) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = fetchBitmap(sURL);
                Message message = handler.obtainMessage(1, bitmap);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }


    @SuppressWarnings("unused")
    public static byte[] ThumbImg(String imgUrl) 
    {

        //first check in the cache, if not available then store in the sd card memory
            HttpURLConnection connection = null;
            String userAgent = null;

            try
            {
                URL url = new URL(imgUrl);
                connection = ( HttpURLConnection ) url.openConnection();
                 if(userAgent != null) {
                     connection.setRequestProperty("User-Agent", userAgent);
                 }
                 connection.setConnectTimeout(5000);
                 connection.setReadTimeout(5000);
                    int CHUNKSIZE = 8192;        //size of fixed chunks
                int BUFFERSIZE = 1024;       //size of reading buffer


                 int bytesRead = 0;
                 byte[] buffer = new byte[BUFFERSIZE];   //initialize buffer
                 byte[] fixedChunk = new byte[CHUNKSIZE]; //initialize 1st chunk
                 ArrayList<byte[]> BufferChunkList = new ArrayList<byte[]>(); // List of chunk data
                 int spaceLeft = CHUNKSIZE;
                 int chunkIndex = 0;

                 DataInputStream in = new DataInputStream(connection.getInputStream() );

                 while( ( bytesRead = in.read( buffer ) ) != -1 ) { //loop until the DataInputStream is completed
                     if(bytesRead > spaceLeft) {
                         //copy to end of current chunk
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, spaceLeft);
                         BufferChunkList.add(fixedChunk);

                         //create a new chunk, and fill in the leftover
                         fixedChunk = new byte[CHUNKSIZE];
                         chunkIndex = bytesRead - spaceLeft;
                         System.arraycopy(buffer, spaceLeft, fixedChunk, 0, chunkIndex);
                     } else {
                         //plenty of space, just copy it in
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, bytesRead);
                         chunkIndex = chunkIndex + bytesRead;
                     }
                     spaceLeft = CHUNKSIZE - chunkIndex;
                 }

                 if (in != null) {
                     in.close();
                 }

                 // copy it all into one big array
                 int responseSize = (BufferChunkList.size() * CHUNKSIZE) + chunkIndex;  
                 Log.d("response size",""+responseSize);
                 byte[] responseBody = new byte[responseSize];
                 int index = 0;
                 for(byte[] b : BufferChunkList) {
                     System.arraycopy(b, 0, responseBody, index, CHUNKSIZE);
                     index = index + CHUNKSIZE;
                 }
                 System.arraycopy(fixedChunk, 0, responseBody, index, chunkIndex);

                return responseBody;                     


            }catch(SocketTimeoutException se)
            {

            }
            catch(Exception e)
            {

                e.printStackTrace();
            }finally
            {
                if(connection!=null)
                connection.disconnect();
            }

        return null;
    }

}

请注意,这并不保证解决OOM问题。显示大位图并非总是一个好主意。
另一个选项是使用 BitmapFactory.Options inSampleSize参数。

@frieza:但是在我的代码中何时何地使用SoftReferences呢?你能否解释一下? - Nikki
@frieza:你能否请修改你的示例代码,以便我根据你的示例代码进行更改时不会出现错误。 - Nikki
正如我也使用了这个:private DrawableManager() { drawableMap = new HashMap(); } 其中DrawableManager是一个类并调用构造函数。 - Nikki
@frieza:在添加您提供的代码时......它在这一行中出现错误imageView.setImageBitmap((Bitmap) drawableMap.get(sURL));,因为无法将软件引用转换为位图。 - Nikki
@frieza:在大多数使用“bm”进行转换的SoftwareRefernces中显示错误。 - Nikki
显示剩余11条评论

0

请问您能告诉我在我的代码中应该在哪里以及如何包含这个示例(如上所示)以运行吗? - Nikki
根据链接的SO响应,由于Honeycomb Bitmap是在VM堆上分配的,因此如果您的minSDK大于13,则这不再是一个解决方案。 - Gregriggins36

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接