JessYanCoding / JessYanCoding/AndroidAutoSize

Fresco 加载为圆形图片,图片偏移错位不居中问题分析!

Offen
#209 10 Kommentare 3 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Java
Sterne
13.7k
Forks
2k
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

autosize在已经修改了activity的Density的情况下.fresco虽然持有了修改后的Density值,但是在完成图片加载二进制数据流,缓存为 bitmap对象的时候没有对创建的bitmap应用新的Density.
此段代码可以断点调试com.facebook.imagepipeline.memory.BucketsBitmapPool#alloc方法,
贴出此方法源码.

```
@Override
protected Bitmap alloc(int size) {
return Bitmap.createBitmap(
1,
(int) Math.ceil(size / (double) BitmapUtil.RGB_565_BYTES_PER_PIXEL),
Bitmap.Config.RGB_565);
}

```
创建的bitmap调用的是不传DisplayMetrics display,对象的创建构造函数,则会给bitmap赋值为系统Density值.
且在之后的使用此bitmap创建BitmapDrawable的时候
创建时机在
com.facebook.drawee.drawable.RoundedBitmapDrawable 的构造中
```
public RoundedBitmapDrawable(Resources res, @Nullable Bitmap bitmap, @Nullable Paint paint) {
super(new BitmapDrawable(res, bitmap));
mBitmap = bitmap;
if (paint != null) {
mPaint.set(paint);
}

mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
}
```
,因为BitmapDrawable的源码android.graphics.drawable.BitmapDrawable#computeBitmapSize方法
```
private void computeBitmapSize() {
final Bitmap bitmap = mBitmapState.mBitmap;
if (bitmap != null) {
mBitmapWidth = bitmap.getScaledWidth(mTargetDensity);
mBitmapHeight = bitmap.getScaledHeight(mTargetDensity);
} else {
mBitmapWidth = mBitmapHeight = -1;
}
}
```

会根据传入resourse对象和bitmap中的Density对比来缩放或者扩大生成的BitmapDrawable

后面的逻辑无需跟进了.太过复杂,都是些矩阵之类的缩放计算在onDraw方法中,只需要知道
在这里构造方法中创建的BitmapDrawable的时候因为传入的 Density和传入的bitmap中记录的
Density不同导致创建BitmapDrawable的宽高发生变动.

-----------------------------------------------------------------
到这里似乎我看到了解决的曙光.
于是乎开始试验.我试着从重写
com.facebook.imagepipeline.memory.BucketsBitmapPool#alloc方法,入手
于是我继承了生成BucketsBitmapPool类对象的PoolFactory工厂类.重写其getBitmapPool方法

```
mBitmapPool =
new BucketsBitmapPool(
mConfig.getMemoryTrimmableRegistry(),
DefaultBitmapPoolParams.get(),
mConfig.getBitmapPoolStatsTracker());
```
加入了
```
mBitmapPool =
new BucketsBitmapPool(
mConfig.getMemoryTrimmableRegistry(),
DefaultBitmapPoolParams.get(),
mConfig.getBitmapPoolStatsTracker()){
@Override protected Bitmap alloc(int size) {
Bitmap mAlloc = super.alloc(size);
if (mAlloc != null) {
mAlloc.setDensity(MyApplication.getContext().getResources().getDisplayMetrics().densityDpi);
}
return mAlloc;
};
```
很遗憾的是没有效果.重新调试发现.源码中又一次重新生成了bitmap.具体可以看
com.facebook.imagepipeline.platform.DefaultDecoder#decodeFromStream方法
此方法中
```
@Nullable Bitmap **bitmapToReuse** = null;
......
bitmapToReuse = mBitmapPool.get(sizeInBytes);
```
就是我上面处理后的bitmap.可惜的是在比方法后半段.源码有重新生成 bitmap.
```
Bitmap decodedBitmap = null;
......................
**options.inBitmap** = **bitmapToReuse**;
decodedBitmap = BitmapFactory.decodeStream(inputStream, null, options);
```
而且这里重新生成的bitmap的inDensity又被重置了.具体可以看源码方法追踪
--------------------------
到此上面的方法已经不可行.难道逼我去修改源码吗?很难过.
中间省略 6个小时.知道半夜5点多的追踪和分析和查资料.
最后发现重新生成的bitmap可以在某一个fresce愿意对外提供配置的对象里面可以拿到.
具体配置方法我就直接贴代码了,提供的 方法可以按照自己的理解去修改,注意一定配置
setImageDecoder 设置才可以解决设置新的autosize的计算的density
```

public static ImagePipelineConfig getFrescoConfigFix(Context mContext) {
String IMAGE_PIPELINE_CACHE_DIR = "image_cache";
String IMAGE_PIPELINE_SMALL_CACHE_DIR = "image_small_cache";
int MAX_DISK_SMALL_CACHE_SIZE = 10 * ByteConstants.MB;
int MAX_DISK_SMALL_ONLOWDISKSPACE_CACHE_SIZE = 5 * ByteConstants.MB;
//----------------------------------------------------------------------
Supplier mBitmapMemoryCacheParamsSupplier =
new BitmapMemoryCacheParamsSupplier(
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE));
//----------------------------------------------------------------------
MemoryTrimmableRegistry mMemoryTrimmableRegistry = NoOpMemoryTrimmableRegistry.getInstance();
mMemoryTrimmableRegistry.registerMemoryTrimmable(new MemoryTrimmable() {
@Override // 当内存紧张时采取的措施
public void trim(MemoryTrimType trimType) {
final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();
MLog.i("Fresco onCreate suggestedTrimRatio = " + suggestedTrimRatio);

if (MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio
|| MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio()
== suggestedTrimRatio
|| MemoryTrimType.OnSystemLowMemoryWhileAppInForeground.getSuggestedTrimRatio()
== suggestedTrimRatio
) {
// 清除内存缓存
Fresco.getImagePipeline().clearMemoryCaches();
}
}
});

//----------------------------------------------------------------------
/*
* 推荐缓存到应用本身的缓存文件夹,这么做的好处是:
* 1、当应用被用户卸载后能自动清除缓存
* 2、一些内存清理软件可以扫描出来,进行内存的清理
*/
File fileCacheDir = mContext.getCacheDir();
DiskCacheConfig mMainDiskCacheConfig = DiskCacheConfig.newBuilder(mContext)
.setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR)
.setBaseDirectoryPath(fileCacheDir)
.build();

DiskCacheConfig mSmallImageDiskCacheConfig = DiskCacheConfig.newBuilder(mContext)
.setBaseDirectoryPath(fileCacheDir)
.setBaseDirectoryName(IMAGE_PIPELINE_SMALL_CACHE_DIR)
.setMaxCacheSize(MAX_DISK_SMALL_CACHE_SIZE)
.setMaxCacheSizeOnLowDiskSpace(MAX_DISK_SMALL_ONLOWDISKSPACE_CACHE_SIZE)
.build();
//----------------------------------------------------------------------
AppComponent appComponent = ArtUtils.obtainAppComponentFromContext(mContext);
OkHttpNetworkFetcher mNetworkFetcher = new OkHttpNetworkFetcher(appComponent.okHttpClient());
//----------------------------------------------------------------------
Set mRequestListeners = new HashSet<>();
mRequestListeners.add(new RequestLoggingListener());

//----------------------------------------------------------------------
ImagePipelineConfig mConfig = ImagePipelineConfig
.newBuilder(mContext)
.setBitmapMemoryCacheParamsSupplier(mBitmapMemoryCacheParamsSupplier)
.setMemoryTrimmableRegistry(mMemoryTrimmableRegistry)
.setMainDiskCacheConfig(mMainDiskCacheConfig) // 设置主磁盘配置
.setSmallImageDiskCacheConfig(mSmallImageDiskCacheConfig) // 设置小图的磁盘配置
.setBitmapsConfig(Bitmap.Config.ARGB_8888) // 若不是要求忒高清显示应用,就用使用RGB_565吧(默认是ARGB_8888)
.setNetworkFetcher(mNetworkFetcher)
.setRequestListeners(mRequestListeners)

.build();
//----------------------------------------------------------------------
PlatformDecoder mPlatformDecoder =
PlatformDecoderFactory.buildPlatformDecoder(
mConfig.getPoolFactory(), mConfig.getExperiments().isGingerbreadDecoderEnabled());
//----------------------------------------------------------------------
PlatformBitmapFactory mPlatformBitmapFactory =
PlatformBitmapFactoryProvider.buildPlatformBitmapFactory(
mConfig.getPoolFactory(), mPlatformDecoder);
//----------------------------------------------------------------------
CountingMemoryCache mBitmapCountingMemoryCache =
BitmapCountingMemoryCacheFactory.get(
mConfig.getBitmapMemoryCacheParamsSupplier(),
mConfig.getMemoryTrimmableRegistry(),
mConfig.getBitmapMemoryCacheTrimStrategy());
//----------------------------------------------------------------------
final AnimatedFactory animatedFactory = AnimatedFactoryProvider.getAnimatedFactory(
mPlatformBitmapFactory,
mConfig.getExecutorSupplier(),
mBitmapCountingMemoryCache,
mConfig.getExperiments().shouldDownscaleFrameToDrawableDimensions());
//----------------------------------------------------------------------
ImageDecoder gifDecoder = null;
ImageDecoder webPDecoder = null;

if (animatedFactory != null) {
gifDecoder = animatedFactory.getGifDecoder(mConfig.getBitmapConfig());
webPDecoder = animatedFactory.getWebPDecoder(mConfig.getBitmapConfig());
}
//----------------------------------------------------------------------
ImageDecoder mImageDecoder;
if (mConfig.getImageDecoderConfig() == null) {
mImageDecoder = new DefaultImageDecoder(
gifDecoder,
webPDecoder,
mPlatformDecoder) {
@Override
public CloseableImage decode(EncodedImage encodedImage, int length, QualityInfo qualityInfo,
ImageDecodeOptions options) {
CloseableImage mDecode = super.decode(encodedImage, length, qualityInfo, options);
if (mDecode instanceof CloseableStaticBitmap) {
Bitmap mUnderlyingBitmap = ((CloseableStaticBitmap) mDecode).getUnderlyingBitmap();
if (mUnderlyingBitmap != null) {
mUnderlyingBitmap
.setDensity(mContext.getResources().getDisplayMetrics().densityDpi);
}
}
return mDecode;
}
};
} else {
mImageDecoder = new DefaultImageDecoder(
gifDecoder,
webPDecoder,
mPlatformDecoder,
mConfig.getImageDecoderConfig().getCustomImageDecoders()) {
@Override
public CloseableImage decode(EncodedImage encodedImage, int length, QualityInfo qualityInfo,
ImageDecodeOptions options) {
CloseableImage mDecode = super.decode(encodedImage, length, qualityInfo, options);
if (mDecode instanceof CloseableStaticBitmap) {
Bitmap mUnderlyingBitmap = ((CloseableStaticBitmap) mDecode).getUnderlyingBitmap();
if (mUnderlyingBitmap != null) {
mUnderlyingBitmap
.setDensity(mContext.getResources().getDisplayMetrics().densityDpi);
}
}
return mDecode;
}
};
// Add custom image formats if needed
ImageFormatChecker.getInstance()
.setCustomImageFormatCheckers(
mConfig.getImageDecoderConfig().getCustomImageFormats());
}
//----------------------------------------------------------------------
return ImagePipelineConfig.newBuilder(mContext)
.setBitmapMemoryCacheParamsSupplier(mBitmapMemoryCacheParamsSupplier) // 设置内存配置
.setMemoryTrimmableRegistry(mMemoryTrimmableRegistry) // 报内存警告时的监听
.setMainDiskCacheConfig(mMainDiskCacheConfig) // 设置主磁盘配置
.setSmallImageDiskCacheConfig(mSmallImageDiskCacheConfig) // 设置小图的磁盘配置
.setBitmapsConfig(Bitmap.Config.ARGB_8888) // 若不是要求忒高清显示应用,就用使用RGB_565吧(默认是ARGB_8888)
.setNetworkFetcher(mNetworkFetcher)
.setRequestListeners(mRequestListeners)
.setDownsampleEnabled(true) // 在解码时改变图片的大小,支持PNG、JPG以及WEBP格式的图片,与ResizeOptions配合使用
.setImageDecoder(mImageDecoder)
.build();
}
```

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.