我們來(lái)看下這三個(gè)值的初始值分別是什么
inTargetDensity
通過(guò)搜索,這個(gè)值只在decodeResourceStream方法中進(jìn)行了賦值操作
......
if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
......
從代碼中可以看出,inTargetDensity的取值是獲取當(dāng)前手機(jī)的densityDpi,即當(dāng)前的手機(jī)運(yùn)行環(huán)境的手機(jī)密度
inScreenDensity
搜索整個(gè)BitmapFactory的所有代碼,發(fā)現(xiàn)這個(gè)變量并沒(méi)有賦值的地方,只有一個(gè)使用的地方setDensityFromOptions
private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
if (outputBitmap == null || opts == null) return;
final int density = opts.inDensity;
if (density != 0) {
outputBitmap.setDensity(density);
final int targetDensity = opts.inTargetDensity;
//對(duì)desity和targetDensity進(jìn)行比較
if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
return;
}
byte[] np = outputBitmap.getNinePatchChunk();
final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
if (opts.inScaled || isNinePatch) {
outputBitmap.setDensity(targetDensity);
}
} else if (opts.inBitmap != null) {
// bitmap was reused, ensure density is reset
outputBitmap.setDensity(Bitmap.getDefaultDensity());
}
}
從而,我們可以知道,這個(gè)值默認(rèn)的情況下一直是0。
inDensity
同樣的,搜索BitmapFactory的源碼,可以搜索到賦值的地方是:
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
......
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;
}
}
......
return decodeStream(is, pad, opts);
}
這個(gè)方法是在decodeResource中調(diào)用
public static Bitmap decodeResource(Resources res, int id, Options opts) {
......
final TypedValue value = new TypedValue();
//通過(guò)資源的id去加載圖片資源的信息,并保存在TypedValue中
is = res.openRawResource(id, value);
bm = decodeResourceStream(res, value, is, null, opts);
.....
}
這里代碼其實(shí)就是分為兩步:
- 通過(guò)資源的id去加載合適的資源,并把一些參數(shù)保存在TypedValue對(duì)象中,如圖片的對(duì)應(yīng)文件夾的density
- 通過(guò)第1步拿到資源流,去讀取我們需要的圖片
從上面的代碼可以看到density的值是把從資源中讀取的density賦值給它,其實(shí)就是我們的圖片資源所在drawable對(duì)應(yīng)的density,也就是下面表格的對(duì)應(yīng)的值:

三個(gè)值的作用
從上面的賦值中,我們知道了這三個(gè)變量表示的意思,那么我們來(lái)看看這個(gè)三個(gè)變量是怎么使用的。
public final int getByteCount() {
return getRowBytes() * getHeight();
}
public final int getRowBytes() {
......
return nativeRowBytes(mNativePtr);
}
翻閱nativeRowBytes這個(gè)native的源碼,可以知道一個(gè)bitmap對(duì)象在運(yùn)行時(shí)所占內(nèi)存大小是:bitmap占用的寬 * bitmap占用的高*每個(gè)像素占用的字節(jié),那么這里的寬高又是什么呢?圖片的寬高嗎?我們來(lái)看下decodeResourceStream方法的源碼:
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
validate(opts);
if (opts == null) {
opts = new Options();
}
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;
}
}
if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
return decodeStream(is, pad, opts);
}
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
......
Bitmap bm = null;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
bm = nativeDecodeAsset(asset, outPadding, opts);
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}
......
return bm;
}
調(diào)用nativeDecodeAsset或者decodeStreamInternal方法,這兩個(gè)方法最終都會(huì)調(diào)用到BitmapFactory.cpp中的doDecode方法
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
jobject options, bool allowPurgeable, bool forcePurgeable = false) {
if (options != NULL) {
if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
const int density = env->GetIntField(options, gOptions_densityFieldID);
const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
//代碼1
if (density != 0 && targetDensity != 0 && density != screenDensity) {
scale = (float) targetDensity / density;
}
}
}
//代碼2
int scaledWidth = decodingBitmap.width();
int scaledHeight = decodingBitmap.height();
//代碼3
if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
scaledWidth = int(scaledWidth * scale + 0.5f);
scaledHeight = int(scaledHeight * scale + 0.5f);
}
// update options (if any)
if (options != NULL) {
env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
env->SetObjectField(options, gOptions_mimeFieldID,
getMimeTypeString(env, decoder->getFormat()));
}
}
從上面的代碼中:
代碼1處的代碼:
根據(jù)density,targetDensity,screenDensity來(lái)計(jì)算縮放的比例scale,scale = (float) targetDensity / density
標(biāo)志2處的代碼:獲取原始圖片的原始寬高
標(biāo)志3處的代碼:根據(jù)上面計(jì)算的縮放比例scale,重新計(jì)算實(shí)際的寬高。那這個(gè)有什么作用呢?
總結(jié):
這三個(gè)參數(shù)使用來(lái)計(jì)算bitmap對(duì)象的寬高的,總結(jié)來(lái)說(shuō)一個(gè)bitmap的占用內(nèi)存為:
bitmap的寬=原圖片的寬 / inSampleSize * (inTargetDensity / inDensity+0.5f)
bitmap的高=原圖片的高 / inSampleSize * (inTargetDensity / inDensity+0.5f)
bitmap所占內(nèi)存的大小=bitmap的寬*bitmap的高*每個(gè)像素所占內(nèi)存