Bitmap是位圖,每個(gè)像素點(diǎn)根據(jù)位的深度表示不同種類的顏色,深度越大,代表的顏色值越多,存儲(chǔ)空間越大。
BitmapFactory工廠
從文件讀取。
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeFile(String pathName, Options opts)
根據(jù)路徑,創(chuàng)建一個(gè)FileInputStream,然后,調(diào)用decodeStream方法。
從資源讀取。
public static Bitmap decodeResource(Resources res, int id, Options opts)
public static Bitmap decodeResource(Resources res, int id)
public static Bitmap decodeResourceStream(Resources res, TypedValue value,InputStream is, Rect pad, Options opts)
前兩個(gè)方法,通過(guò)Resources的openRawResource(id, value)方法,根據(jù)資源id,調(diào)用AssetManager的openNonAsset方法,創(chuàng)建AssetInputStream。然后,他們?cè)偃フ{(diào)用decodeResourceStream方法。最終,這三個(gè)方法全部觸發(fā)decodeStream。
從字節(jié)數(shù)組讀取。
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
調(diào)用本地nativeDecodeByteArray方法。
從流讀取。
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeStream(InputStream is)
資源解析id成AssetInputStream流,調(diào)用本地nativeDecodeAsset方法。其他流調(diào)用decodeStreamInternal方法,調(diào)用本地nativeDecodeStream方法。
從文件描述符讀取。
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd)
調(diào)本地nativeDecodeFileDescriptor方法,或創(chuàng)建FileInputStream流解析,與文件讀取類似,調(diào)用decodeStreamInternal方法。
綜上所述
BitmapFactory提供了解析文件、資源、字節(jié)數(shù)組、文件描述符、或流的方式創(chuàng)建Bitmap,在創(chuàng)建時(shí),Option配置將影響它占用的內(nèi)存。以上就是幾種主要的解析方式,下面我們根據(jù)源碼看一下InputStream是如何解析。
InputStream流解析
看一下BitmapFactory的decodeStream方法。
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
Bitmap bm = null;
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
bm = nativeDecodeAsset(asset, outPadding, opts);//根據(jù)InputStream不同。
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}
...
setDensityFromOptions(bm, opts);
} finally {
}
return bm;
}
它的三個(gè)參數(shù),InputStream、Rect和Option,Options是工廠的靜態(tài)內(nèi)部類,包含一些解析的配置信息,將這些信息傳到底層。AssetInputStream流調(diào)用nativeDecodeAsset方法,其他的調(diào)用nativeDecodeStream方法。
private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
return nativeDecodeStream(is, tempStorage, outPadding, opts);
}
字節(jié)數(shù)組大小16 * 1024。
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
jobject padding, jobject options) {
jobject bitmap = NULL;
SkAutoTDelete<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage))
if (stream.get()) {
SkAutoTDelete<SkStreamRewindable> bufferedStream(
SkFrontBufferedStream::Create(stream.detach(), BYTES_TO_BUFFER));
bitmap = doDecode(env, bufferedStream, padding, options);
}
return bitmap;
}
該JNI#方法的第一個(gè)參數(shù)是InputStream數(shù)據(jù)輸入流,第二個(gè)參數(shù)是storage字節(jié)數(shù)組,padding是null,第四個(gè)參數(shù)是Bitmap配置Options。然后,將上層傳入的InputStream轉(zhuǎn)換底層SkStreamRewindable,JNI#方法doDecode解析bufferedStream,生成一個(gè)jobject對(duì)象,它就是Java層的Bitmap。看一下JNI#方法doDecode。該方法比較長(zhǎng),分成四個(gè)部分。
第一部分代碼
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
int sampleSize = 1;
SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
SkColorType prefColorType = kN32_SkColorType;
bool doDither = true;
bool isMutable = false;
float scale = 1.0f;
bool preferQualityOverSpeed = false;
bool requireUnpremultiplied = false;
if (options != NULL) {
//解析Option的inSampleSize,圖片讀取到內(nèi)存中的像素壓縮的比例。
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
//解析Option的inJustDecodeBounds,只獲取圖片信息,不讀像素模式。
if (optionsJustBounds(env, options)) {
decodeMode = SkImageDecoder::kDecodeBounds_Mode;
}
//初始化Option的outWidth與outHeight為-1。outMimeType為0。
env->SetIntField(options, gOptions_widthFieldID, -1);
env->SetIntField(options, gOptions_heightFieldID, -1);
env->SetObjectField(options, gOptions_mimeFieldID, 0);
//Bitmap.Config配置inPreferredConfig。
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
//解析出SkColorType。
prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
//Java層值讀取
isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
preferQualityOverSpeed = env->GetBooleanField(options,gOptions_preferQualityOverSpeedFieldID);
requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
//Java層inBitmap對(duì)象
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
//Option的inScaled,默認(rèn)true
if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
//Option的inDensity
const int density = env->GetIntField(options, gOptions_densityFieldID);
//Option的inTargetDensity
const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
//Option的inScreenDensity
const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
if (density != 0 && targetDensity != 0 && density != screenDensity) {
scale = (float) targetDensity / density;
}
}
}
const bool willScale = scale != 1.0f;
...
//后續(xù)代碼
}
當(dāng)Java層Option參數(shù)不空時(shí)候,讀取參數(shù)信息,主要Option信息包括。
inSampleSize,圖片讀取到內(nèi)存中的像素采樣率,即圖片的壓縮比,inSampleSize是2代表圖像的長(zhǎng)寬都變?yōu)樵嫉?/2,該值根據(jù)圖片的真實(shí)像素寬高和ImageView的寬高共同計(jì)算得出。
inJustDecodeBounds,該參數(shù)表示解析時(shí)只讀取圖片信息,如圖片尺寸,不分配像素內(nèi)存空間,SkImageDecoder模式是kDecodeBounds_Mode。
isMutable,可變的,生成Bitmap可變位圖,可修改像素,默認(rèn)是false。
inPreferredConfig:Bitmap.Config圖片像素類型,基本類型有ALPHA_8、RGB_565、ARGB_4444、ARGB_8888。默認(rèn)是ARGB_8888,它的圖片質(zhì)量較高,每像素4個(gè)字節(jié),內(nèi)存占用最大。可配置成RGB_565類型,2個(gè)字節(jié)。
outHeight/outWidth,圖像寬高,初始值設(shè)置-1。
inBitmap,重用bitmap,如果在Java層設(shè)置了這個(gè)參數(shù),新Bitmap會(huì)使用該參數(shù)的內(nèi)存空間,Option#isMutable必須是可變的。
prefColorType,根據(jù)上層Config的nativeInt變量,解析SkColorType類型,與Config像素類型對(duì)應(yīng),kRGB_565_SkColorType、kIndex_8_SkColorType、kN32_SkColorType等。
inScaled,inDensity,inTargetDensity,inScreenDensity,由它們決定圖片是否縮放,首先是inScaled決定是否縮放,默認(rèn)縮放,然后,如果配置了inTargetDensity和inDensity,并且inScreenDensity和inDensity不相等,按照比例inTargetDensity/inDensity縮放,圖片去適應(yīng)屏幕。最后的結(jié)果scale不是1,將設(shè)置willScale參數(shù)。
inDensity,圖片像素密度。
inTargetDensity,目標(biāo)像素密度。根據(jù)該值與Bitmap的像素密度的比值,確定縮放比例。
inScreenDensity,屏幕像素密度。
當(dāng)我們從資源加載圖片時(shí),設(shè)置inTargetDensity值為densityDpi,因此,不同屏幕像素密度手機(jī)從資源文件加載圖片時(shí)縮放比不同,載入內(nèi)存也是不同的。densityDpi值以160像素為基準(zhǔn),240,320,480,640。
第二部分代碼
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
//decoder解析器是空返回null
//SkImageDecoder設(shè)置sampleSize、doDither、
//preferQualityOverSpeed、requireUnpremultiplied等參數(shù)。
decoder->setSampleSize(sampleSize);
...
android::Bitmap* reuseBitmap = nullptr;
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
//這個(gè)bitmap不可變
if (reuseBitmap->peekAtPixelRef()->isImmutable()) {//不可變的bitmap,無(wú)法重用
javaBitmap = NULL;
reuseBitmap = nullptr;
} else {
existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
}
}
...
JavaPixelAllocator javaAllocator(env);
RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
(SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
if (!willScale) {
decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
decoder->setAllocator(outputAllocator);
} else if (javaBitmap != NULL) {
decoder->setAllocator(&scaleCheckingAllocator);
}
}
創(chuàng)建解析器SkImageDecoder,設(shè)置從Option讀取到部分配置。獲取上層重用javaBitmap的底層Bitmap對(duì)象,如果不可變,不能重用,置空。如果可重用,解析javaBitmap內(nèi)存字節(jié)大小existingBufferSize。
在非kDecodeBounds_Mode時(shí),讀取像素到內(nèi)存,SKImageDecoder的setAllocator方法,設(shè)置Allocator內(nèi)存分配器。包括三種,JavaPixelAllocator,RecyclingPixelAllocator和ScaleCheckingAllocator。
當(dāng)不需要縮放時(shí),重用Bitmap,使用RecyclingPixelAllocator,不重用Bitmap,使用JavaPixelAllocator。
當(dāng)需要縮放,重用Bitmap,使用ScaleCheckingAllocator。
只有RecyclingPixelAllocator和ScaleCheckingAllocator傳入existingBufferSize大小,與重用Bitmap相關(guān)。
第三部分代碼
SkAutoTDelete<SkImageDecoder> add(decoder);
AutoDecoderCancel adc(options, decoder);
SkBitmap decodingBitmap;
//stream解析到skbitmap
if (decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)
!= SkImageDecoder::kSuccess) {
return nullObjectReturn("decoder->decode returned false");
}
//拿到寬高
int scaledWidth = decodingBitmap.width();
int scaledHeight = decodingBitmap.height();
//Scale處理后的寬高
if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
scaledWidth = int(scaledWidth * scale + 0.5f);
scaledHeight = int(scaledHeight * scale + 0.5f);
}
//得到了寬和高和mimeType,設(shè)置option
if (options != NULL) {
jstring mimeType = getMimeTypeString(env, decoder->getFormat());
if (env->ExceptionCheck()) {
return nullObjectReturn("OOM in getMimeTypeString()");
}
env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
}
//inJustDecodeBounds為true,直接返回NULL
if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) {
return NULL;
}
//NinePatchPeeker相關(guān)處理
...
SkImageDecode的decode方法,解碼stream后,得到SkBitmap類型的對(duì)象。
獲取SkBitmap的寬高,如果需要縮放,并且非kDecodeBounds_Mode模式,將重新計(jì)算的scaledWidth、scaledHeight和mimeType信息設(shè)置到Java層Option對(duì)象。
如果是kDecodeBounds模式,表示Java層Option設(shè)置inJustDecodeBounds參數(shù),不需要分配內(nèi)存,返回空,圖片信息已保存在Option對(duì)象。
第四部分代碼
SkBitmap outputBitmap;
if (willScale) {
//計(jì)算Scale的比例。
const float sx = scaledWidth / float(decodingBitmap.width());
const float sy = scaledHeight / float(decodingBitmap.height());
SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());
outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
colorType, decodingBitmap.alphaType()));
if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
return nullObjectReturn("allocation failed for scaled bitmap");
}
if (outputAllocator != &javaAllocator) {
outputBitmap.eraseColor(0);
}
SkPaint paint;
paint.setFilterQuality(kLow_SkFilterQuality);
SkCanvas canvas(outputBitmap);
canvas.scale(sx, sy);
canvas.drawARGB(0x00, 0x00, 0x00, 0x00);
canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
} else {
outputBitmap.swap(decodingBitmap);
}
if (outputBitmap.pixelRef() == NULL) {
return nullObjectReturn("Got null SkPixelRef");
}
if (!isMutable && javaBitmap == NULL) {
outputBitmap.setImmutable();
}
if (javaBitmap != NULL) {
bool isPremultiplied = !requireUnpremultiplied;
GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
outputBitmap.notifyPixelsChanged();
return javaBitmap;
}
int bitmapCreateFlags = 0x0;
if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
// now create the java bitmap
return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
SkImageInfo是一個(gè)結(jié)構(gòu)體,包含寬高、顏色類型、透明類型等信息,Make方法,創(chuàng)建該結(jié)構(gòu)體,SKBitmap的setInfo方法設(shè)置內(nèi)部SkImageInfo對(duì)象。
如果不縮放,將前面解析的decodingBitmap交給輸出outputBitmap。
如果重用javaBitmap不空,利用輸出的outputBitmap重新初始化該對(duì)象,通知像素改變,將該Java層Bitmap對(duì)象返回。
如果不重用,GraphicsJNI的createBitmap方法,創(chuàng)建Java層的Bitmap實(shí)例。
jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap,
int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
int density) {
...
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
ninePatchChunk, ninePatchInsets);
return obj;
}
env的NewObject方法,創(chuàng)建Bitmap實(shí)例,入?yún)⑹莕ative層bitmap指針,Java層字節(jié)數(shù)組的強(qiáng)引用,bitmap寬/高,density,isMutable,isPremultiplied,ninePatchChunk,ninePatchInsets。
Java層字節(jié)數(shù)組強(qiáng)引用是mPixelStorage中結(jié)構(gòu)體java的jbyteArray變量。在使用JavaPixelAllocator分配器分配內(nèi)存時(shí),會(huì)介紹如何對(duì)結(jié)構(gòu)體賦值。
// called from JNI
Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density, boolean isMutable,
boolean requestPremultiplied, byte[] ninePatchChunk,
NinePatch.InsetStruct ninePatchInsets)
最終,Java層Bitmap實(shí)例創(chuàng)建成功,構(gòu)造方法是由底層JNI方法調(diào)用。
了解SKImageDecode解碼
我們前面說(shuō)過(guò),通過(guò)解碼器SkImageDecoder的decode方法,解析stream,得到SkBitmap對(duì)象。
SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
SkColorType pref,Mode mode) {
fShouldCancelDecode = false;
fDefaultPref = pref;
SkBitmap tmp;
const Result result = this->onDecode(stream, &tmp, mode);
if (kFailure != result) {
bm->swap(tmp);
}
return result;
}
調(diào)用的onDecode方法在它的子類里實(shí)現(xiàn),子類包括各種圖片格式png,jpeg及gif的解碼類。比如,SkPNGImageDecoder繼承SkImageDecoder,看一下它的onDecode方法。
SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream,
SkBitmap* decodedBitmap,Mode mode) {
//初始化
if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
return kFailure;
}
....
//該模式直接返回成功
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
return kSuccess;
}
....
//內(nèi)存分配
//最終觸發(fā)Allocator的allocPixelRef方法,入?yún)ecodedBitmap
if (!this->allocPixelRef(decodedBitmap,
kIndex_8_SkColorType == colorType ? colorTable : NULL)) {
return kFailure;
}
png_read_update_info(png_ptr, info_ptr);
....
//像素讀取
....
png_read_end(png_ptr, info_ptr);
....
return kSuccess;
}
調(diào)用它自己的allocPixelRef方法。
bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
SkColorTable* ctable) const {
return bitmap->tryAllocPixels(fAllocator, ctable);
}
該方法調(diào)用入?yún)kBitmap的tryAllocPixels方法,將SkImageDecoder內(nèi)部的分配器傳入。在SkImageDecoder創(chuàng)建后會(huì)setAllocator初始化Allocator,前面代碼都有寫(xiě)。
bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
HeapAllocator stdalloc;
if (NULL == allocator) {
allocator = &stdalloc;
}
return allocator->allocPixelRef(this, ctable);
}
最終,在SkBitmap類中,調(diào)用Allocator的allocPixelRef方法,將SKBitmap作為參數(shù)傳入。以JavaPixelAllocator分配器為例,在Java堆上分配內(nèi)存。
class JavaPixelAllocator : public SkBitmap::Allocator {
public:
JavaPixelAllocator(JNIEnv* env);
~JavaPixelAllocator();
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
android::Bitmap* getStorageObjAndReset() {
android::Bitmap* result = mStorage;
mStorage = NULL;
return result;
};
private:
JavaVM* mJavaVM;
android::Bitmap* mStorage = nullptr;
};
JavaPixelAllocator的allocPixelRef方法。
bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
JNIEnv* env = vm2env(mJavaVM);
mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
return mStorage != nullptr;
}
GraphicsJNI的allocateJavaPixelRef方法。創(chuàng)建底層Bitmap對(duì)象,指針賦值mStorage,在JavaPixelAllocator中保存。
android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env,
SkBitmap* bitmap,SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
size_t size;
if (!computeAllocationSize(*bitmap, &size)) {
return NULL;
}
const size_t rowBytes = bitmap->rowBytes();
jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(
gVMRuntime, gVMRuntime_newNonMovableArray,
gByte_class, size);//Java堆分配內(nèi)存
jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime,
gVMRuntime_addressOf, arrayObj);//地址
android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
info, rowBytes, ctable);//創(chuàng)建native層Bitmap
wrapper->getSkBitmap(bitmap);//用native層Bitmap初始化SkBitmap
bitmap->lockPixels();
return wrapper;
}
GraphicsJNI的computeAllocationSize方法,根據(jù)SkBitmap的height和width計(jì)算分配空間大小,存儲(chǔ)在size。
VMRuntime的newNonMovableArray方法,在Java堆分配內(nèi)存,返回Java層字節(jié)數(shù)組arrayObj。
VMRuntime的addressOf方法,返回jbyteArray字節(jié)數(shù)組arrayObj的地址。
VMRuntime源碼/libcore/libart/src/main/java/dalvik/system/VMRuntime.java目錄。
創(chuàng)建底層的Bitmap。
Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
: mPixelStorageType(PixelStorageType::Java) {
env->GetJavaVM(&mPixelStorage.java.jvm);
mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj);
mPixelStorage.java.jstrongRef = nullptr;//數(shù)組強(qiáng)引用暫時(shí)置空。
mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
mPixelRef->unref();
}
底層Bitmap的兩個(gè)變量mPixelStorage和mPixelRef。
mPixelStorage,聯(lián)合體,內(nèi)部的java結(jié)構(gòu)體內(nèi)容。jweakRef指向NewWeakGlobalRef方法創(chuàng)建的Java層字節(jié)數(shù)組(Java堆對(duì)象)的弱全局引用。mPixelRef,WrappedPixelRef,繼承SkPixelRef,封裝Bitmap本身,地址addr,像素大小rowBytes,SkImageInfo等。
struct {
JavaVM* jvm;
jweak jweakRef;
jbyteArray jstrongRef;
} java;
底層Bitmap創(chuàng)建后,調(diào)用getSkBitmap方法,初始化SkBitmap輸出。
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
outBitmap->setPixelRef(refPixelRefLocked())->unref();//獲取mPixelRef引用
outBitmap->setHasHardwareMipMap(hasHardwareMipMap());
}
底層Bitmap的refPixelRefLocked方法,獲取mPixelRef引用。SkBitmap內(nèi)部包含mPixelRef引用。

綜上所述
Android6.0的Bitmap源碼,位圖像素存儲(chǔ)在Java堆字節(jié)數(shù)組中,Java層的Bitmap對(duì)象引用底層Bitmap,字節(jié)數(shù)組同時(shí)被Java層Bitmap和底層Bitmap#mPixelStorage引用。
任重而道遠(yuǎn)