JessYanCoding / JessYanCoding/AndroidAutoSize
Fresco 加载为圆形图片,图片偏移错位不居中问题分析!
- 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.