JessYanCoding / JessYanCoding/AndroidAutoSize

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

Abierto
#209 10 comentarios 3 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Java
Estrellas
13.7k
Forks
2k
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

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();
}
```

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.