Lottie

Mapping of JSON files to objects

  1. The LottieCompositionFactory class provides several static methods for loading JSON data from different ways and parsing it toLottieComposition
  2. Methods ending in “Sync” load and parse json data synchronously in the calling thread; The rest of the methods are customized through LottieAsyncTaskA subclassAsyncCompositionLoaderLoad and parse JSON data asynchronously.
  3. Lottie is used both asynchronously and synchronouslyJsonReaderTo parse the JSON data, avoiding OOM problems associated with loading the entire JSON data into memory at once.
  4. The actual JSON parsing is performedLottieCompositionParserparseMethods. This method parses the fields in JSON toLottieCompositionA member variable of theLottieCompositionThe instance

Parsing external resources

{" v ", "5.1.10", "fr" / / bodymovin version: 24, / / frame rate "IP" : 0, / / the start key frames "op" : 277, / / end key frames "w" : 110, / / animation width "h" : Height 110, / / animation "nm" : "synthesis of 2," "DDD" : 0, "assets" : [...]. // Layers: [...] // Layer info}Copy the code

In the outermost layer, you can get the version number of the bodyMovin plug-in, the start keyframe, the animation frame rate, and the width and height of the animation.

Resolution images

{" id ":" image_0 ", "w" / / photo id: 750, / / picture width "h" : 1334, / / picture height "u" : "images /, / / image path" p ":" img_0. PNG / / picture name}"Copy the code

The image resource is in the “asset” and can have multiple images, each image data is parsed into the entity class LottieImageAsset

Parsing the layer

"The layers" : [{" DDD ": 0," ind ": 1, / / layer id" ty ": 2, / / layer type" nm ":" eye - right 2 ", "parent" : 3, "refId" : "Image_0", / / reference resource Id "sr" : 1, "ks" : {/ / animation attribute value "s" : {/ / s: scaling of the data values "a" : 0, "k" : [100, 100, 100], "ix" : 6}...}, "IP" : 0, //inFrame startFrame "op": 241, //outFrame end frame "st": 0, //startFrame startFrame "bm": 0},Copy the code

A complex image can be represented by multiple layers, with each layer showing a portion of the content, and the content in the layer can also be broken down into multiple elements. In the Json that Lottie interprets, layers can be divided into the following categories: PreComp, Solid, Image, Null, Shape, and Text. During data parsing, the data entity class Layer will be converted to distinguish which Layer is based on type.

From LottieCompisition to CompositionLayer

The second step in using Lottie is to set the data object LottieComposition, which converts the data of the data object into a BaseLayer with drawing capability. The following is part of the class diagram of the transformation:

insufficient

1. When mask and matters are present, performance is greatly affected

You can check the source code in BaseLayer to see how the mask or matte is calculated:

If there is no mask or matte, the drawLayer call returns, but for mask or matte saveLayer is performed before drawLayer is drawn.

In saveLayer, you can see that this is a very time-consuming operation that requires allocating and drawing an offscreen buffer and more than doubles the cost of rendering.

So the smaller the mask or matte boundary, the smaller the performance loss.

2. Decode the picture in the main thread

Lottie ImageAssetManager

@Nullable public Bitmap bitmapForId(String id) { Bitmap bitmap = bitmaps.get(id); if (bitmap == null) { LottieImageAsset imageAsset = imageAssets.get(id); if (imageAsset == null) { return null; } if (delegate ! = null) { bitmap = delegate.fetchBitmap(imageAsset); if (bitmap ! = null) { bitmaps.put(id, bitmap); } return bitmap; } InputStream is; try { if (TextUtils.isEmpty(imagesFolder)) { throw new IllegalStateException("You must set an images folder before loading an image." + " Set it with LottieComposition#setImagesFolder or LottieDrawable#setImagesFolder"); } is = context.getAssets().open(imagesFolder + imageAsset.getFileName()); } catch (IOException e) { Log.w(L.TAG, "Unable to open asset.", e); return null; } BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inScaled = true; opts.inDensity = 160; bitmap = BitmapFactory.decodeStream(is, null, opts); bitmaps.put(id, bitmap); } return bitmap; }Copy the code

If no image proxy is set here, the image is decoded directly. If an image proxy is set up, the image is retrieved from the agent listener.

mLottieAnim.setImageAssetDelegate(new ImageAssetDelegate() {
          @Override
          public Bitmap fetchBitmap(LottieImageAsset asset) {
              try {
                    FileInputStream fileInputStream = new FileInputStream("/sdcard/data/images/" + asset.getFileName());
                  return BitmapFactory.decodeStream(fileInputStream);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return null;
          }
      });
Copy the code

Both methods, which will be called in the main thread, will be decoded first from the file and later directly from the cache. For some Android low-end machines may not be able to play animation, or even cause ANR;

Perhaps Lottie is more concerned with situations where there are no or very few images, such as some vector animations, where this performance would be fine.

Mapping of data objects to Drawable

AnimatableLayer is derived from Drawable. Let’s look at its subclasses:

Where, LayerView corresponds to Layer data, which includes:

Lottie playback process

draw

LottieAnimationView inherits from AppCompatImageView and encapsulates some animation operations, such as:

The specific draw delegate is LottieDrawable. Let’s look at the Draw () method in LottieDrawable:

LottieDrawable inherits from AnimatableLayer with the draw() method as follows:

You can see that the contents of this layer are drawn first, and then the contents of the contained layers are drawn: