Fresco初始化之前會有一些自定義的相關配置需要開發人員自己 去設置,設置配置的對象是ImagePipelineConfig,現在開始分析ImagePipelineConfig中第一個配置參數AnimatedImageFactory的相關知識點,以下代碼是使用Fresco時,設置配置屬性的需要的代碼demo。
<pre>
Set<RequestListener> listeners = new HashSet<>();
// 添加請求日志監聽包含請求及請求過程
listeners.add(new RequestLoggingListener());
// 設置配置屬性
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
.setRequestListeners(listeners)
.build();
</pre>
<pre>
//true
@Nullable private final AnimatedImageFactory mAnimatedImageFactory;
//error
@NonNull private final AnimatedImageFactory mAnimatedImageFactory = null;
</pre>
上述代碼是在ImagePipelineConfig設置的AnimatedImageFactory屬性
知識點@Nullable與@NonNull
@Nullable標記當前對象可以為null值,如果使用@NonNull則表示當前屬性不能為null值,如果給它賦值null,則會編譯器報錯。
AnimatedImageFactory是一個接口,它的主要作用是對帶動畫的圖像進行解碼,它的實現是AnimatedImageFactoryImpl,具體代碼如下(可以直接跳過,代碼后是相關的知識點):
<pre>
public interface AnimatedImageFactory {
public CloseableImage decodeGif(
final EncodedImage encodedImage,
final ImageDecodeOptions options,
final Bitmap.Config bitmapConfig);
public CloseableImage decodeWebP(
final EncodedImage encodedImage,
final ImageDecodeOptions options,
final Bitmap.Config bitmapConfig);
}
</pre>
<pre>
public class AnimatedImageFactoryImpl implements AnimatedImageFactory {
private final AnimatedDrawableBackendProvider mAnimatedDrawableBackendProvider;
private final PlatformBitmapFactory mBitmapFactory;
static AnimatedImageDecoder sGifAnimatedImageDecoder = null;
static AnimatedImageDecoder sWebpAnimatedImageDecoder = null;
private static AnimatedImageDecoder loadIfPresent(final String className) {
try {
Class<?> clazz = Class.forName(className);
return (AnimatedImageDecoder) clazz.newInstance();
} catch (Throwable e) {
return null;
}
}
static {
sGifAnimatedImageDecoder = loadIfPresent("com.facebook.animated.gif.GifImage");
sWebpAnimatedImageDecoder = loadIfPresent("com.facebook.animated.webp.WebPImage");
}
public AnimatedImageFactoryImpl(
AnimatedDrawableBackendProvider animatedDrawableBackendProvider,
PlatformBitmapFactory bitmapFactory) {
mAnimatedDrawableBackendProvider = animatedDrawableBackendProvider;
mBitmapFactory = bitmapFactory;
}
/**
Decodes a GIF into a CloseableImage.
@param encodedImage encoded image (native byte array holding the encoded bytes and meta data)
@param options the options for the decode
@param bitmapConfig the Bitmap.Config used to generate the output bitmaps
-
@return a {@link CloseableImage} for the GIF image
*/
public CloseableImage decodeGif(
final EncodedImage encodedImage,
final ImageDecodeOptions options,
final Bitmap.Config bitmapConfig) {
if (sGifAnimatedImageDecoder == null) {
throw new UnsupportedOperationException("To encode animated gif please add the dependency " +
"to the animated-gif module");
}
final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef();
Preconditions.checkNotNull(bytesRef);
try {
Preconditions.checkState(!options.forceOldAnimationCode);
final PooledByteBuffer input = bytesRef.get();
AnimatedImage gifImage = sGifAnimatedImageDecoder.decode(input.getNativePtr(), input.size());return getCloseableImage(options, gifImage, bitmapConfig);
} finally {
CloseableReference.closeSafely(bytesRef);
}
}
/**
- Decode a WebP into a CloseableImage.
- @param encodedImage encoded image (native byte array holding the encoded bytes and meta data)
- @param options the options for the decode
- @param bitmapConfig the Bitmap.Config used to generate the output bitmaps
- @return a {@link CloseableImage} for the WebP image
*/
public CloseableImage decodeWebP(
final EncodedImage encodedImage,
final ImageDecodeOptions options,
final Bitmap.Config bitmapConfig) {
if (sWebpAnimatedImageDecoder == null) {
throw new UnsupportedOperationException("To encode animated webp please add the dependency " +
"to the animated-webp module");
}
final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef();
Preconditions.checkNotNull(bytesRef);
try {
Preconditions.checkArgument(!options.forceOldAnimationCode);
final PooledByteBuffer input = bytesRef.get();
AnimatedImage webPImage = sWebpAnimatedImageDecoder.decode(
input.getNativePtr(),
input.size());
return getCloseableImage(options, webPImage, bitmapConfig);
} finally {
CloseableReference.closeSafely(bytesRef);
}
}
private CloseableAnimatedImage getCloseableImage(
ImageDecodeOptions options,
AnimatedImage image,
Bitmap.Config bitmapConfig) {
List<CloseableReference<Bitmap>> decodedFrames = null;
CloseableReference<Bitmap> previewBitmap = null;
try {
int frameForPreview = options.useLastFrameForPreview ? image.getFrameCount() - 1 : 0;
if (options.decodeAllFrames) {
decodedFrames = decodeAllFrames(image, bitmapConfig);
previewBitmap = CloseableReference.cloneOrNull(decodedFrames.get(frameForPreview));
}
if (options.decodePreviewFrame && previewBitmap == null) {
previewBitmap = createPreviewBitmap(image, bitmapConfig, frameForPreview);
}
AnimatedImageResult animatedImageResult = AnimatedImageResult.newBuilder(image)
.setPreviewBitmap(previewBitmap)
.setFrameForPreview(frameForPreview)
.setDecodedFrames(decodedFrames)
.build();
return new CloseableAnimatedImage(animatedImageResult);
} finally {
CloseableReference.closeSafely(previewBitmap);
CloseableReference.closeSafely(decodedFrames);
}
}
private CloseableReference<Bitmap> createPreviewBitmap(
AnimatedImage image,
Bitmap.Config bitmapConfig,
int frameForPreview) {
CloseableReference<Bitmap> bitmap = createBitmap(
image.getWidth(),
image.getHeight(),
bitmapConfig);
AnimatedImageResult tempResult = AnimatedImageResult.forAnimatedImage(image);
AnimatedDrawableBackend drawableBackend =
mAnimatedDrawableBackendProvider.get(tempResult, null);
AnimatedImageCompositor animatedImageCompositor = new AnimatedImageCompositor(
drawableBackend,
new AnimatedImageCompositor.Callback() {
@Override
public void onIntermediateResult(int frameNumber, Bitmap bitmap) {
// Don't care.
}
@Override
public CloseableReference<Bitmap> getCachedBitmap(int frameNumber) {
return null;
}
});
animatedImageCompositor.renderFrame(frameForPreview, bitmap.get());
return bitmap;
}
private List<CloseableReference<Bitmap>> decodeAllFrames(
AnimatedImage image,
Bitmap.Config bitmapConfig) {
final List<CloseableReference<Bitmap>> bitmaps = new ArrayList<>();
AnimatedImageResult tempResult = AnimatedImageResult.forAnimatedImage(image);
AnimatedDrawableBackend drawableBackend =
mAnimatedDrawableBackendProvider.get(tempResult, null);
AnimatedImageCompositor animatedImageCompositor = new AnimatedImageCompositor(
drawableBackend,
new AnimatedImageCompositor.Callback() {
@Override
public void onIntermediateResult(int frameNumber, Bitmap bitmap) {
// Don't care.
}
@Override
public CloseableReference<Bitmap> getCachedBitmap(int frameNumber) {
return CloseableReference.cloneOrNull(bitmaps.get(frameNumber));
}
});
for (int i = 0; i < drawableBackend.getFrameCount(); i++) {
CloseableReference<Bitmap> bitmap = createBitmap(
drawableBackend.getWidth(),
drawableBackend.getHeight(),
bitmapConfig);
animatedImageCompositor.renderFrame(i, bitmap.get());
bitmaps.add(bitmap);
}
return bitmaps;
}
@SuppressLint("NewApi")
private CloseableReference<Bitmap> createBitmap(
int width,
int height,
Bitmap.Config bitmapConfig) {
CloseableReference<Bitmap> bitmap = mBitmapFactory.createBitmap(width, height, bitmapConfig);
bitmap.get().eraseColor(Color.TRANSPARENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
bitmap.get().setHasAlpha(true);
}
return bitmap;
}
}
</pre>
AnimatedImageFactoryImpl代碼相關知識點
知識點final變量
final相關資料
【final關鍵字的使用方法】:
【修飾變量】:
final成員變量表示常量,只能被賦值一次,賦值后值不再改變。
【修飾方法】:
final方法不能被子類方法覆蓋,但可以被繼承。
【修飾類】:
final類不能被繼承,沒有子類,final類中所有方法都是final的。
<pre>
public class MyFinal {
private static final int finalValue = 10;
public static void main(String[] args) {
//error final修飾的變量賦值后不可以被改變
finalValue = 11;
}
}
</pre>
知識點反射獲取類對象
AnimatedImageFactoryImpl靜態塊利用反射的方式初始化了兩個AnimatedImageDecoder(接口),它們實際指向的是GifImage和WebPImage對象。
<pre>
static AnimatedImageDecoder sGifAnimatedImageDecoder = null;
static AnimatedImageDecoder sWebpAnimatedImageDecoder = null;
private static AnimatedImageDecoder loadIfPresent(final String className) {
try {
//通過反射創建類實例
Class<?> clazz = Class.forName(className);
return (AnimatedImageDecoder) clazz.newInstance();
} catch (Throwable e) {
return null;
}
}
static {
sGifAnimatedImageDecoder = loadIfPresent("com.facebook.animated.gif.GifImage");
sWebpAnimatedImageDecoder = loadIfPresent("com.facebook.animated.webp.WebPImage");
}
</pre>
知識點@SuppressLint
SuppressLint作用是屏蔽android lint錯誤在Android代碼中,我們有時會使用比我們在AndroidManifest中設置的android:minSdkVersion版本更高的方法,此時編譯器會提示警告.解決方法是在方法上加上@SuppressLint("NewApi")作用僅僅是屏蔽android lint錯誤,所以在方法中還要判斷版本做不同的操作.
<pre>
@SuppressLint("NewApi")
private CloseableReference<Bitmap> createBitmap(
int width,
int height,
Bitmap.Config bitmapConfig) {
CloseableReference<Bitmap> bitmap = mBitmapFactory.createBitmap(width, height, bitmapConfig);
bitmap.get().eraseColor(Color.TRANSPARENT);
// @SuppressLint("NewApi")檢測到大于最小版本的api了
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
bitmap.get().setHasAlpha(true);
}
return bitmap;
}
</pre>
以上包含了AnimatedImageFactoryImpl的相關知識點,那么接著分析AnimatedImageFactoryImpl中相關屬性中包含的知識點,這個類中包含4個屬性,其中兩個是AnimatedImageDecoder,它們用來解碼webp和gif的,剩下兩個是AnimatedDrawableBackendProvider、PlatformBitmapFactory,具體的解釋在后續的知識點分析中再做解釋。
AnimatedImageFactoryImpl中decodeGif方法分析
<pre>
public CloseableImage decodeGif(
final EncodedImage encodedImage,//圖片編碼對象
final ImageDecodeOptions options,//圖像解碼選項
final Bitmap.Config bitmapConfig) //Bitmap配置信息{
final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef();
try {
//省略部分驗證代碼
final PooledByteBuffer input = bytesRef.get();
AnimatedImage gifImage = sGifAnimatedImageDecoder.decode(input.getNativePtr(), input.size());
return getCloseableImage(options, gifImage, bitmapConfig);
} finally {
CloseableReference.closeSafely(bytesRef);
}
}
</pre>
首先我們先分析獲取NativePooledByteBuffer過程中使用的知識點
<pre>
//這里從EncodeImage對象中獲取了一個CloseableReference
//泛型類型實際指向的是NativePooledByteBuffer,它實現了PooledByteBuffer
final CloseableReference<PooledByteBuffer> bytesRef = encodedImage.getByteBufferRef();
//獲取NativePooledByteBuffer對象
final PooledByteBuffer input = bytesRef.get();
</pre>
在上述代碼中返回了一個CloseableReference,CloseableReference實現了Cloneable和Closeable接口,然后調用了CloseableReference的get方法獲取NativePooledByteBuffer對象。
<pre>
public final class CloseableReference<T> implements Cloneable, Closeable {
//省略部分代碼
public synchronized T get() {
//從SharedReference獲取NativePooledByteBuffer對象
return mSharedReference.get();
}
}
</pre>
知識點Cloneable
一.Cloneable 的用途
Cloneable和Serializable一樣都是標記型接口,它們內部都沒有方法和屬性,implements Cloneable表示該對象能被克隆,能使用Object.clone()方法。如果沒有implements Cloneable的類調用Object.clone()方法就會拋出CloneNotSupportedException。
二.克隆的分類
(1)淺克隆(shallow clone),淺拷貝是指拷貝對象時僅僅拷貝對象本身和對象中的基本變量,而不拷貝對象包含的引用指向的對象。
(2)深克隆(deep clone),深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。
下面是一個淺克隆的demo
<pre>
public class MyClone implements Cloneable {
String name = "wyd";
public static void main(String[] args) {
MyClone myClone = new MyClone();
try {
//克隆一個當前對象
MyClone copyMyClone = (MyClone) myClone.clone();
//修改當前克隆對象中的參數值
copyMyClone.name = "wydcopy";
System.out.println("copyMyClone.name is " + copyMyClone.name + ", myClone.name is " + myClone.name);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
</pre>
輸出結果 copyMyClone.name is wydcopy, myClone.name is wyd,從上面的結果可以看出克隆的對象如果發生變化,那么不會影響到原始對象。
知識點Closeable
Closeable接口也定義了close()方法。實現了Closeable接口的類的對象可以被關閉。從JDK7開始,Closeable擴展了AutoCloseable。因此,在JDK7中,所有實現了Closeable接口的類也都實現了AutoCloseable接口。
AutoCloseable接口對JDK7新添加的帶資源的try語句提供了支持,這種try語句可以自動執行資源關閉過程。只有實現了AutoCloseable接口的類的對象才可以由帶資源的try語句進行管理。AutoCloseable接口只定義了close()方法:
void close() throws Exception
這個方法關閉調用對象,釋放可能占用的所有資源。在帶資源的try語句的末尾,會自動調用該方法,因此消除了顯式調用close()方法的需要。
<pre>
//JDK1.7以前的版本,釋放資源的寫法
//這段代碼需要自己手動調用close方法去釋放
static String readFirstLineFromFile(String path) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null)
br.close();
}
return null;
}
//JDK1.7中的寫法,利用AutoCloseable接口
//這段代碼會自動調用close,不再需要手動調用了
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
</pre>
以上代碼可以看出JDK1.7后因為所有的可被關閉資源對象都實現了AutoCloseable接口,因此,只要在try的表達式中加入了資源對象,那么就不在需要close當前對象了,close的方法會被系統自動調用。
接下來分析CloseableReference上述中調用SharedReference的知識點
<pre>
public class SharedReference<T> {
@GuardedBy("itself")
private static final Map<Object, Integer> sLiveObjects = new IdentityHashMap<>();
private static void addLiveReference(Object value) {
synchronized (sLiveObjects) {
Integer count = sLiveObjects.get(value);
if (count == null) {
sLiveObjects.put(value, 1);
} else {
sLiveObjects.put(value, count + 1);
}
}
}
private static void removeLiveReference(Object value) {
synchronized (sLiveObjects) {
Integer count = sLiveObjects.get(value);
if (count == null) {
// Uh oh.
FLog.wtf(
"SharedReference",
"No entry in sLiveObjects for value of type %s",
value.getClass());
} else if (count == 1) {
sLiveObjects.remove(value);
} else {
sLiveObjects.put(value, count - 1);
}
}
}
}
</pre>
知識點IdentityHashMap
IdentityHashMap是無序的Map集合,可以添加重復的key的Map,它的key比較的是內存地址,如果兩個key的內存地址是相同的,則不能重復;如果兩個key的內存地址不一樣,如new出來的對象,這個時候兩個值完全一樣的key也是可以添加進去的。
<pre>
public class MyIdentityHashMap {
public static void main(String[] args) {
Map<Object, Integer> map = new IdentityHashMap<>();
//內存地址相等則后面的會覆蓋前面的key
map.put("aaa", 10);
map.put("aaa", 11);
//內存地址相等則后面的會覆蓋前面的key
map.put(1, 14);
map.put(1, 15);
//內存地址不想等則不會被覆蓋
map.put(new String("aaa"), 12);
map.put(new String("aaa"), 13);
Set set = map.keySet();
for (Object str :
set) {
System.out.println(map.get(str));
}
System.out.println("aaa" == "aaa");
//輸出結果
// 11
// 13
// 12
// 15
// true
}
}
</pre>
知識點@GuardedBy("itself")
@GuardedBy("itself")表示當前注釋的變量的同步由變量本身去控制
<pre>
@GuardedBy("itself")//標注變量的同步由變量本身去控制
private static final Map<Object, Integer> sLiveObjects = new IdentityHashMap<>();
private static void addLiveReference(Object value) {
// 同步自身
synchronized (sLiveObjects) {}
}
</pre>
接下來分析解碼方法中使用的知識點,相關代碼如下
<pre>
//sGifAnimatedImageDecoder代表GifImage對象,實際調用的是GifImage中的decode方法
AnimatedImage gifImage = sGifAnimatedImageDecoder.decode(input.getNativePtr(), input.size());
</pre>
GifImage對象中decode方法代碼如下
<pre>
@ThreadSafe
@DoNotStrip
public class GifImage implements AnimatedImage, AnimatedImageDecoder {
private volatile static boolean sInitialized;
// Accessed by native methods
@SuppressWarnings("unused")
@DoNotStrip
private long mNativeContext;
//判斷是否初始化了gifimage本地庫
private static synchronized void ensure() {
if (!sInitialized) {
sInitialized = true;
//下面代碼實際調用的是System.loadLibrary(libraryName)
SoLoaderShim.loadLibrary("gifimage");
}
}
//解碼方法
@Override
public AnimatedImage decode(long nativePtr, int sizeInBytes) {
return GifImage.create(nativePtr, sizeInBytes);
}
//創建GifImage對象
public static GifImage create(long nativePtr, int sizeInBytes) {
ensure();
Preconditions.checkArgument(nativePtr != 0);
return nativeCreateFromNativeMemory(nativePtr, sizeInBytes);
}
//通過本地內存創建GifImage
private static native GifImage nativeCreateFromNativeMemory(long nativePtr, int sizeInBytes);
</pre>
知識點@SuppressWarnings("unused")
如果使用 SuppressWarnings("unused") 注釋了某個變量,那么表示當前這個變量是預留變量,在當前類中沒有使用。
知識點System.loadLibrary(libraryName)
加載本地庫,只有加載了本地庫才可以調用jni中的方法,否則會報錯,這里調用的是nativeCreateFromNativeMemory本地方法
知識點volatile關鍵字
可見性:
可見性是一種復雜的屬性,因為可見性中的錯誤總是會違背我們的直覺。通常,我們無法確保執行讀操作的線程能適時地看到其他線程寫入的值,有時甚至是根本不可能的事情。為了確保多個線程之間對內存寫入操作的可見性,必須使用同步機制。
可見性,是指線程之間的可見性,一個線程修改的狀態對另一個線程是可見的。也就是一個線程修改的結果。另一個線程馬上就能看到。比如:用volatile修飾的變量,就會具有可見性。volatile修飾的變量不允許線程內部緩存和重排序,即直接修改內存。所以對其他線程是可見的。但是這里需要注意一個問題,volatile只能讓被他修飾內容具有可見性,但不能保證它具有原子性。比如 volatile int a = 0;之后有一個操作 a++;這個變量a具有可見性,但是a++ 依然是一個非原子操作,也就這這個操作同樣存在線程安全問題。
原子性:
原子是世界上的最小單位,具有不可分割性。比如 a=0;(a非long和double類型) 這個操作是不可分割的,那么我們說這個操作時原子操作。再比如:a++; 這個操作實際是a = a + 1;是可分割的,所以他不是一個原子操作。非原子操作都會存在線程安全問題,需要我們使用同步技術(sychronized)來讓它變成一個原子操作。一個操作是原子操作,那么我們稱它具有原子性。java的concurrent包下提供了一些原子類,我們可以通過閱讀API來了解這些原子類的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。
volatile原理:
當把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的,因此不會將該變量上的操作與其他內存操作一起重排序。volatile變量不會被緩存在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型的變量時總會返回最新寫入的值。
在訪問volatile變量時不會執行加鎖操作,因此也就不會使執行線程阻塞,因此volatile變量是一種比sychronized關鍵字更輕量級的同步機制。
當對非volatile變量進行讀寫的時候,每個線程先從內存拷貝變量到CPU緩存中。如果計算機有多個CPU,每個線程可能在不同的CPU上被處理,這意味著每個線程可以考慮到不同的CPU cache中。
而聲明變量是volatile的,JVM保證了每次讀變量都從內存中讀,跳過CPU cache這一步。
從以上的一些知識點中其實可以分析出來,Fresco解碼gif是在native塊做的,webp的流程和gif的流程類似,那么ImagePipelineConfig中AnimatedImageFactory屬性的職責就是解碼webp、gif圖片。