Like attention, no more lost, your support means a lot to me!
🔥 Hi, I’m Chouchou. GitHub · Android-Notebook has been included in this article. Welcome to grow up with Chouchou Peng. (Contact information at GitHub)
preface
- Recently, I took charge of some requirements related to album. While completing the business, I also hope to have a deeper understanding of the picture loading process.
- In this article, I will discuss the process of loading images from the source code, the core source code cited in the article I have been simplified and combed, I believe that can greatly reduce your learning costs. Please be sure to like and follow if you can help, it really means a lot to me.
Related articles
- The graphics | new-confucianism. PNG besides lossless what else do you know?”
- The Android | cliche! Screen adaptation principle & plan summary notes”
- The Android | so-accurately weighed various! Tell me about the whole process of loading picture”
directory
1. Picture decoding options
public static class Options { public Bitmap inBitmap; public boolean inMutable; public boolean inJustDecodeBounds; public boolean inPremultiplied; public int inDensity; public int inTargetDensity; public int inScreenDensity; Public Boolean inScaled; public int inSampleSize; public int outWidth; public int outHeight; public Bitmap.Config outConfig; public ColorSpace outColorSpace; }Copy the code
public Options() {
inScaled = true;
inPremultiplied = true;
}
Copy the code
2. Overview of image resource loading process
Now let’s look at the entry method for loading image resources:
BitmapFactory.java
Public static Bitmap decodeResource(Resources res, int id) {return decodeResource(res, id, null); } public static Bitmap decodeResource(Resources res, int id, Options opts) { Open InputStream final TypedValue value = new TypedValue(); InputStream is = res.openRawResource(id, value); Return decodeResourceStream(res, value, is, NULL, OPTS); }Copy the code
As you can see, the difference between the two entry methods is whether Options are passed in or not. In Section 5 I’ll talk about customizing decoding by configuring Options. For now, we’ll assume opts == null.
Simplified decodeResource(…) It’s very clear that there are two steps:
- 1. Match the resource and open InputStream
- 2. Decode the resource and return the Bitmap
3. Step 1: Match resources and start InputStream
This step is mainly to locate the resource from the resource ID (an int value) to a specific folder:
ResourcesImpl.java
-> 1, match the resource ID, open InputStream finally call (simplified) : InputStream openRawResource(@rawRes int id, TypedValue value) {1.1 Matching resource getValue(id, value, true); Return massets.opennonasset (value.assetcookie, value.string.tostring (), assetManager.access_streaming); return massets.opennonasset (value.assetcookie, value.string.tostring (), assetManager.access_streaming); } -> 1.1 void getValue(@anyres int id, TypedValue outValue, Boolean resolveRefs) { Boolean found = mAssets. GetResourceValue (id, 0, outValue, resolveRefs); if (! Found) {1.1.2 Throw new NotFoundException("Resource ID #0x" + inteer.tohexString (ID)); }}Copy the code
AssetManager.java
-> 1.1.1
boolean getResourceValue(@AnyRes int resId, int densityDpi, TypedValue outValue, boolean resolveRefs) {
final int cookie = nativeGetResourceValue(mObject, resId, (short) densityDpi, outValue, resolveRefs);
if (cookie <= 0) {
return false;
}
return true;
}
Copy the code
AssetManager.cpp
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, jshort density, Jobject typeD_value, jboolean resolve_references) {jobject typed_value, jboolean resolve_references) return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } static jint CopyValue(JNIEnv* env, ... jobject out_typed_value) { ... if (config ! = nullptr) { To save folder corresponding density in TypeValue env - > SetIntField (out_typed_value, gTypedValueOffsets mDensity, config - > density); } return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie)); }Copy the code
The above code has been very simplified, focusing on the following points:
- In branch 1.1.1, look up the resource ID and store the relevant information in outValue. The key information is: Store the folder’s densityDpi in TypeValue (which will be used in the next section);
- The matching process is rather lengthy, so look at the schematic diagram:
Citing theBlog.csdn.net/xuaho0907/a…– Alpaca who loves ice cream
4. Step 2: Decode the resource and return the Bitmap
InputStream & TypedValue (with densityDpi for folder); decodeResourceStream()
BitmapFactory.java
-> 2. Public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) { if (opts == null) { opts = new Options(); } 2.1 densityDpi if (opts.indensity == 0 && value! = null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density ! = typedValue.density_none) {opts.indensity = density; DensityDpi if (opts.intargetDensity == 0 && res! = null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; Return decodeStream(is, pad, OPTS); } -> decodeStream public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {2.3.1 AssetManager input stream (for example: / asset, / raw/drawable) if (is instanceof AssetManager. AssetInputStream) {final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); return nativeDecodeAsset(asset, outPadding, opts, Options.nativeInBitmap(opts), Options.nativeColorSpace(opts)); } else {decodeStreamInternal(is, outPadding, opts); Private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Opts) {block of reusable intermediate memory byte [] tempStorage = null; if (opts ! = null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; return nativeDecodeStream(is, tempStorage, outPadding, opts, Options.nativeInBitmap(opts), Options.nativeColorSpace(opts)); }Copy the code
As you can see, there are two important steps before executing decodeStream() :
- 2.1 If inDensity is not set, set it to densityDpi of the folder
- 2.2 If inTargetDensity is not set, set it to the densityDpi of the device
When it comes to [2.3 Performing Decoding], call different native methods depending on whether AssetInputStream is called:
- 2.3.1 AssetManager input stream
(e.g. /asset, /raw, /drawable)
, the callnativeDecodeAsset()
- 2.3.2 Other input streams
(E.g. FileInputStream)
, the callnativeDecodeStream()
BitmapFactory.cpp
Static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, jobject padding, jobject options) { Asset* asset = reinterpret_cast<Asset*>(native_asset); Return doDecode(env, SKSTD ::make_unique<AssetStreamAdaptor>(asset), padding, options); Static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, jobject padding, jobject options) { jobject bitmap = NULL; std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); if (stream.get()) { std::unique_ptr<SkStreamRewindable> bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); Decode bitmap = doDecode(env, STD :: Move (bufferedStream), padding, options); } return bitmap; }Copy the code
It all comes down to doDecode(), which is the core logic for decoding images:
Static jobject doDecode(JNIEnv* env, STD ::unique_ptr<SkStreamRewindable> stream, jobject padding, Jobject options) {1, get Java layer options object In field value int sampleSize; Corresponding to Options#inSampleSize (default 1) if (sampleSize <= 0) {sampleSize = 1; } bool onlyDecodeSize; Corresponding to Options#inJustDecodeBounds (default false) bool isHardware; Corresponding to Options#inPreferredConfig (default ARGB_8888) bool isMutable; Corresponding to Options#inMutable (default false) Jobject javaBitmap; Corresponding to Options#inBitmap (default null) Boolean inScale; Corresponding to Options#inScaled (default true) int density; Corresponding to Options#inDensity int targetDensity; Options#outWidth = -1 Options#outHeight = -1 Options#outMimeType = 0 Options#outConfig = 0 Options#outColorSpace = 0 3, obtain inDensity/inTargetDensity float scale = 1.0 f; if (density ! = 0 && targetDensity ! = 0 && density ! = screenDensity) { scale = (float) targetDensity / density; } if (isMutable &&ishardware) {doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable"); return nullObjectReturn("Cannot create mutable hardware bitmap"); } SkISize size = codec->getSampledDimensions(sampleSize); int scaledWidth = size.width(); int scaledHeight = size.height(); Options#outWidth = scaledWidth Options#outHeight = scaledHeight Options#outMimeType = Options#outConfig = (e.g. ARGB_8888) Options#outColorSpace = (e.g. RGB) InJustDecodeBounds = true, only sample size (no zoom) if (onlyDecodeSize) {return NULlptr; } 7, determine the final scale to the target size (sample first, then scale) bool willScale = false; if (scale ! = 1.0f) {willScale = true; ScaledWidth = Static_cast <int>(scaledWidth * scale + 0.5f); scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f); ScaledHeight = static_cast<int>(scaledHeight * scale + 0.5f); scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f); } android::Bitmap* reuseBitmap = nullptr; if (javaBitmap ! = NULL) { reuseBitmap = &bitmap::toBitmap(env, javaBitmap); } RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); 9. Sampling and decoding to obtain SkBitmap (note: Const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType, alphaType, decodeColorSpace); SkBitmap decodingBitmap; decodingBitmap.setInfo(bitmapInfo); DecodingBitmap. TryAllocPixels (decodeAllocator) 10, execute scaling SkBitmap outputBitmap; If (willScale) {10.1 Determine the different Allocator SkBitmap::Allocator* outputAllocator; if (javaBitmap ! = nullptr) { outputAllocator = &recyclingAllocator; } else { outputAllocator = &defaultAllocator; } SkColorType scaledColorType = decodingBitmap.colortype (); outputBitmap.setInfo(bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType)); OutputBitmap. TryAllocPixels (outputAllocator) 10.3 using Canvas zoom const float scaleX = scaledWidth / float(decodingBitmap.width()); const float scaleY = scaledHeight / float(decodingBitmap.height()); SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy); canvas.scale(scaleX, scaleY); Canvas. DrawBitmap (decodingBitmap, 0.0 f, 0.0 f, & paint); } else { outputBitmap.swap(decodingBitmap); } if (javaBitmap! Nullptr) {return javaBitmap} Hardware Bitmap (from Android 8) if (isHardware) {sk_sp < Bitmap > hardwareBitmap = Bitmap: : allocateHardwareBitmap (outputBitmap); return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); } 13, return point 4: General way return bitmap: : createBitmap (env, defaultAllocator getStorageObjAndReset (), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); }Copy the code
Tip: the entire source code is very long and painful to read. The advantage is that eventually through the source code also found a lot of mistakes/one-sided understanding, but also a lot of harvest. If you can help, please be sure to like and follow, it is really important to me.
The resources
- Android: How much Memory Do Your Bitmaps Take up? — Huo Bing
- “Android development master class · Memory optimization (top/Bottom)” — Zhang Shaowen, produced by Geek Time
- A Miscellany of Android Memory Optimization. By Shaowen Zhang
Recommended reading
- Cryptography | is Base64 encryption algorithm?
- Operating system | interruption & system call is analysed
- Interview questions | back algorithm framework to solve problems
- The interview questions | list questions summary algorithm
- Computer network | graphic DNS & HTTPDNS principle
- Logic | “I know you don’t know!” 10 Logic Interview Questions (part 1)
- Say from Android Android | : text to TextView process
- Android | food tasteless! App Startup may be easier than you think
- Android | enough is enough! How does Glide arrange the life cycle clearly
Creation is not easy, your “three lian” is chouchou’s biggest motivation, we will see you next time!