美文网首页
Fresco知识点笔记03-ImagePipelineConfi

Fresco知识点笔记03-ImagePipelineConfi

作者: Android技术研究 | 来源:发表于2016-07-19 16:23 被阅读946次

    原创-android技术研究-转载请注明出处

    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图片。

    相关文章

      网友评论

          本文标题:Fresco知识点笔记03-ImagePipelineConfi

          本文链接:https://www.haomeiwen.com/subject/kzgsjttx.html