The size of memory used by bitmaps:
Bytes = original image width * (options. InTargetDensity/options. InDensity) * long * original image (options. InTargetDensity/options. InDensity) * each pixel digitsCopy the code
InTargetDensity refers to the current density of the phone. InDensity is the density generated by the drawable directory where the image is located
Use the sample sample rate to compress the Bitmap to the specified width and height (principle is not described here)
// Use the decodeRes method to convert the resource to InJustDecodeBoundstrue, first get the map // slice height, native layer does not return the Bitmap // to calculate the sampleSize, and then set tofalseAnd decode again return to the Bitmap public Bitmap compressBitmapIntoThumbnailPic (Context Context, int res) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds =true;
Bitmap bitmap1 = BitmapFactory.decodeResource(context.getResources(), res, options);
Log.d("tag"."first bitmap == " + bitmap1);
int sampleBitmap = calculateInSampleSize(options, 40, 40);
options.inSampleSize = sampleBitmap;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), res, options);
Log.d("tag"."final == " + bitmap.getByteCount() +
" target real density " + options.inTargetDensity + " folder density " + options.inDensity);
return bitmap;
}
Copy the code
// Compare the required width and height with the current image. Take the smallest scale as sampleSize private int calculateInSampleSize(bitmapFactory. Options Options, int requireW, int requereH) { int sampleSize = 1; int outHeight = options.outHeight; int outWidth = options.outWidth; int rationHeight = Math.round(outHeight / requereH); int rationWidth = Math.round(outWidth / requireW);if (rationHeight > 1 || rationWidth > 1) {
sampleSize = rationHeight > rationWidth ? rationWidth : rationHeight;
}
Log.d("tag"."outHeight = " + outHeight + "outWidth = " + outWidth + "-- -- -- -- -- -- -- --" + sampleSize);
return sampleSize;
}
Copy the code
The above code can normally compress a large image, and then look at the source code, found that the BitmapFactory call is decodeStream(…) This method to process, so want to try to directly process the stream to compress the picture should also be able to, the code is shown below:
/ * * * * * / Bitmap is generated by flow in the form of public Bitmap compressBitmapIntoThumbnailPic (InputStream is) {BitmapFactory. Options Options = new BitmapFactory.Options(); options.inJustDecodeBounds =true; Bitmap finalBitmap = null;if(build.version.sdk_int < build.version_codes.kitkat){// Setin- JustDecodeBounds returns only sizes when native decode Bitmap is empty / / or inputStream - > BufferedInputStream guarantee some reset does not support BitmapFactory. DecodeStream (is null, the options). int sampleBitmap = calculateInSampleSize(options, 80, 80); options.inSampleSize = sampleBitmap; options.inJustDecodeBounds =false;
try {
is.reset();
} catch (IOException e) {
e.printStackTrace();
}
finalBitmap = BitmapFactory.decodeStream(is, null, options);
}else{// the size limit for IS.mark (1024) will be removed from 4.4. - OOM will not appearif (is.markSupported()) {
try {
BitmapFactory.decodeStream(is,null,options);
int sampleBitmap = calculateInSampleSize(options, 40, 40);
options.inSampleSize = sampleBitmap;
options.inJustDecodeBounds = false;
is.reset();
finalBitmap = BitmapFactory.decodeStream(is, null, options);
Log.d("tag"."final == " + finalBitmap.getByteCount() +
" target real density " + options.inTargetDensity + " folder density "+ options.inDensity); } catch (IOException e) { e.printStackTrace(); }}}return finalBitmap;
}
Copy the code
Here is a judgment on the version, just for modal convenience. If you want to set inJustDecodeBounds to false, you can reset the inJustDecodeBounds to “false”. If you want to set inJustDecodeBounds to “false”, you can reset the inJustDecodeBounds to “false”. We set the options to return only the size or null if the stream cannot be decode, which means the stream cannot be decode. Then we realized that the position of the stream will change after it is used. When we decode the stream for the first time, mark method was used. The next time the stream is executed, you must use reset to return the content of the last marker in order to use the stream properly. A bit suddenly realized ~, moved by their own.
/**
* Marks the current position in this input stream. A subsequent call to
* the <code>reset</code> method repositions this stream at the last marked
* position so that subsequent reads re-read the same bytes.
*
* <p> The <code>readlimit</code> arguments tells this input stream to
* allow that many bytes to be read before the mark position gets
* invalidated.
*
* <p> The general contract of <code>mark</code> is that, if the method
* <code>markSupported</code> returns <code>true</code>, the stream somehow
* remembers all the bytes read after the call to <code>mark</code> and
* stands ready to supply those same bytes again if and whenever the method
* <code>reset</code> is called. However, the stream is not required to
* remember any data at all if more than <code>readlimit</code> bytes are
* read from the stream before <code>reset</code> is called.
*
* <p> Marking a closed stream should not have any effect on the stream.
*
* <p> The <code>mark</code> method of <code>InputStream</code> does
* nothing.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.InputStream#reset()* /Copy the code
The reason is that if mark is not called when the stream is created, the size of mark record must be smaller than the whole record. In this case, if you call reset request larger than the size of mark record, you will get OOM. Refer to this solution
/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
*
* <p> The general contract of <code>reset</code> is:
*
* <ul>
* <li> If the method <code>markSupported</code> returns
* <code>true</code>, then:
*
* <ul><li> If the method <code>mark</code> has not been called since
* the stream was created, or the number of bytes read from the stream
* since <code>mark</code> was last called is larger than the argument
* to <code>mark</code> at that last call, then an
* <code>IOException</code> might be thrown.
*
* <li> If such an <code>IOException</code> is not thrown, then the
* stream is reset to a state such that all the bytes read since the
* most recent call to <code>mark</code> (or since the start of the
* file, if <code>mark</code> has not been called) will be resupplied
* to subsequent callers of the <code>read</code> method, followed by
* any bytes that otherwise would have been the next input data as of
* the time of the call to <code>reset</code>. </ul>
*
* <li> If the method <code>markSupported</code> returns
* <code>false</code>, then:
*
* <ul><li> The call to <code>reset</code> may throw an
* <code>IOException</code>.
*
* <li> If an <code>IOException</code> is not thrown, then the stream
* is reset to a fixed state that depends on the particular type of the
* input stream and how it was created. The bytes that will be supplied
* to subsequent callers of the <code>read</code> method depend on the
* particular type of the input stream. </ul></ul>
*
* <p>The method <code>reset</code> for class <code>InputStream</code>
* does nothing except throw an <code>IOException</code>.
*
* @exception IOException if this stream has not been marked or if the
* mark has been invalidated.
* @see java.io.InputStream#mark(int)
* @see java.io.IOException
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
Copy the code
As shown in the figure below, the default threshold size of Mark in 4.3 source code is 1024K, so it is easy to cause OOM for large pictures. After 4.4(inclusive), this case was removed and there is no specific 1024 limit. Here is just a simple study of the principle, in fact we still use a framework like Glide to do the work of loading images. But it’s good to know something about how it works.
There are two more questions about extension,
- Why should inJustDecodeBounds = true return an empty Bitmap Why is the first method to use decodeResource successfully return Bitmap?? Or go to the source code to find the answer:
At the end
Have a doubt or to see the source ah, know it also want to know why ·, the level is limited, error please help to correct