Lottie Android 调研

一、简介:

Lottie是一个可以播放由AE打包的json动画文件的开源库,使用它,你可以简单实现播放利用AE制作的动画。(其实Lottie的本质就是解析.json文文件并通过Canvas方式进行绘制动画)

Lottie Android 调研_第1张图片

图1

二、使用方法:

第一步:制作Ae动画

下载AE 安装(AE2007)地址:pan.baidu.com/s/1c19FLdA

1.安装破解: 
Adobe After Effects CC 2017 安装后不要运行,直接使用 adobe.snr.patch.v2.0-painter.exe 选择产品破解即可

中文语言更改:

安装后默认是英文界面,找到安装目录(默认是:C:\Program Files\Adobe\Adobe After Effects CC 2017\Support Files\AMT)

在 AMT 文件夹内找到 application.xml 文件

使用文本编辑器打开并修改底部一行:

en_GB 为 zh_CN 保存。

 

2.安装bodymovin插件:

该项目地址在github.com/bodymovin/b…

安装这个插件有几种方式,采用安装 zxp installer安装bodymovin.zxp(获取这个文件:下载上面的zip文件,在build/extension目录下,这里已经下载好了)方式,

安装云盘中的aescript+aepluginszxp installer.exe,然后安装bodymovin即可

此时打开AE 在window/extension 文件夹下可以看到bodymovin插件说明就ok了,

3.开启允许脚本写入文件和访问网络:

需要在AE的编辑->首选项->常规中勾选允许脚本写入文件和访问网络(默认不开启)

安装完成后可以通过AE制作动画:如果动画中有图片资源,导出.json文件是会在.json生成路径的同一级目录生成Image图片文件,文件夹内存放文件资源,在使用时需要将文件夹复制到系统项目目录或者本地存储目录使用时需要指定。

Lottie Android 调研_第2张图片

                                                   图二:导出json文件操作有图片资源会一起导出至统一路径

Lottie Android 调研_第3张图片

图三:项目中资源存放路径及指定

注:我的项目中时放到资产目录的

Json文件时放到assets目录中的,json。描述了动画的运行方式。渐变持续时间 透明度变化等…..

第二步:引入项目依赖:

1.在自己项目modulebuild.gradle文件中添加如下代码:

dependencies{ compile 'com.airbnb.android:lottie:2.0.0-beta4' } 

网上很多方式dependencies{compile 'com.airbnb.android:lottie:1.0.1'}此方式只支持在api 14及以上版本,虽然我们的车机是api19 但是项目跑上去就是不行但是在手机上可以(所以强烈建议使用2.0.0及以上版本的库,)

2.直接在布局文件中添加:

Lottie Android 调研_第4张图片

其中lottie_loop属性为是否重复无限期动画,当为true,动画无限次数播放,false,播放一次。

或者把json资源放在app/src/main/assets,也可以这样使用它

LottieAnimationView animationView =(LottieAnimationView) findViewById(R.id.animation_view); 
animationView.setAnimation("hello-world.json"); 
animationView.loop(true); 
animationView.playAnimation(); 

该方法将加载文件并在后台解析动画,在完成后异步开始呈现

方式二:或者从网络上加载jsonObject

LottieAnimationView animationView= (LottieAnimationView)findViewById(R.id.animation_view);

     ...

     LottieComposition composition= LottieComposition.fromJson(getResources(), jsonObject, (composition) -> {

        animationView.setComposition(composition);

        animationView.playAnimation();

     });

//这种方式不推荐,网络加载会比较慢,受网速影响体验不佳

扩展:

你也可以通过API控制动画,并且设置一些监听

Lottie Android 调研_第5张图片

如果你需要频繁使用某一个动画,可以使用LottieAnimationView内置的一个缓存策略: 
LottieAnimationView.setAnimation(String, CacheStrategy)
其中CacheStrategy的值可以是Strong,Weak或者None,它们用来决定LottieAnimationView对已经加载并转换好的动画持有怎样形式的引用(强引用/弱引用).

官方说法

  • 如果没有mask和mattes,那么性能和内存非常好,没有bitmap创建,大部分操作都是简单的cavas绘制,内存占用和绘图效率都远远高于帧动画基本上可以舍弃用GIF去做动画的痛苦了。
  • 如果存在mattes,将会创建2~3个bitmap。bitmap在动画加载到window时被创建,被window删除时回收。所以不宜在RecyclerView中使用包涵mattes或者mask的动画,否则会引起bitmap抖动。除了内存抖动,mattes和mask中必要的bitmap.eraseColor()和canvas.drawBitmap()也会降低动画性能。对于简单的动画,在实际使用时性能不太明显。
  • 如果在列表中使用动画,推荐使用缓存LottieAnimationView.setAnimation(String, CacheStrategy) 。

 

 

三:原理

Lottie使用json文件来作为动画数据源,json文件是通过Bodymovin插件导出的,查看sample中给出的json文件,其实就是把图片中的元素进行来拆分,并且描述每个元素的动画执行路径和执行时间。Lottie的功能就是读取这些数据,然后绘制到屏幕上。首先要解析json,建立数据到对象的映射,然后根据数据对象创建合适的Drawable绘制到view上,动画的实现可以通过操作读取到的元素完成。

具体过程如下所示

json文件——>Component——>Drawable——>View

通过如下3个核心类来来完成整个工作流程,因而使用起来比较简单

  • LottieComposition(json->数据对象)
  • Lottie使用LottieComposition来作为After Effects的数据对象,即把Json文件映射为到LottieComposition,该类中提供了解析json的静态方法
  • LottieDrawable(数据对象->Drawable)

绘制

  • LottieAnimationView(绘制)

操作集合,LottieAnimationView 继承自 AppCompatImageView->Imageview,封装了一些动画的操作,具体的绘制时委托为 LottieDrawable 完成的

AppCompatImageView更强大

 

简单介绍下,通过提供

  • fromAssetFileName(资源file)
  • fromFileSync(异步文件,通常是网络数据)
  • fromJson(直接的json)

通过这三个入口接收json文件、json流,然后异步都通过AsynTask来异步处理,最终核心处理都是在fromJsonSync中进行json数据的解析

 

LottieDrawable(数据对象->Drawable)

先看下继承关系

LottieDrawable extends AnimatableLayer extends Drawable

 

AnimatableLayer

首先看下AnimatableLayer继承了Drawable主要重写了draw,在代码中可以看出,借用canvas的save、restoreToCount来实现像PS那种图层叠加的效果

@Override

      public void draw(@NonNullCanvas canvas) {

        int saveCount =canvas.save();

       applyTransformForLayer(canvas, this);

 

        int backgroundAlpha =Color.alpha(backgroundColor);

        if (backgroundAlpha != 0) {

          int alpha =backgroundAlpha;

          if (this.alpha != null) {

            alpha = alpha *this.alpha.getValue() / 255;

          }

         solidBackgroundPaint.setAlpha(alpha);

          if (alpha > 0) {

           canvas.drawRect(getBounds(), solidBackgroundPaint);

          }

        }

        for (int i = 0; i

         layers.get(i).draw(canvas);

        }

       canvas.restoreToCount(saveCount);

      }

特别是for循环那段,更是体现了将之前json解析的元素图层一层层的画出来,如同PS一般

 

 

好了,AnimatableLayer如其名一样负责将将之前的Layer层动画显示出来,下面再来看LottieDrawable

特别是for循环那段,更是体现了将之前json解析的元素图层一层层的画出来,如同PS一般

好了,AnimatableLayer如其名一样负责将将之前的Layer层动画显示出来,下面再来看

LottieDrawable

这个类的核心方法

 

      void setComposition(LottieCompositioncomposition) {

        if (getCallback() == null) {

          throw new IllegalStateException("You or your view must set a Drawable.Callback before setting thecomposition. This " +  "getsdone automatically when added to an ImageView.                     "+"Either callImageView.setImageDrawable() before setComposition() or call " +

                 "setCallback(yourView.getCallback()) first.");

        }

        //清除之前的数据

        clearComposition();

        this.composition = composition;

       animator.setDuration(composition.getDuration());

        setBounds(0, 0,composition.getBounds().width(), composition.getBounds().height());

        //核心函数:即根据lottieComposition建立多个layerView,此时已经创建好了多个Drawable,并通过List建立的为以lottieDrawable为根的一个drawable树。

        buildLayersForComposition(composition);

        getCallback().invalidateDrawable(this);

      }

该方法在LottieAnimationView中调用,该方法中实际调用的核心函数是

    voidbuildLayersForComposition(LottieComposition composition)

这个函数的重点做的是为AnimatableLayer创建关键信息:

  • 将得到的bitmap(mainBitmap, maskBitmap, matteBitmap)+ layer(通过之前setComposition获得的)信息合成 
    LayerView
  • 将LayerView通过super.addLayer(AnimatableLayer#layers),在AnimatableLayer#layers中去draw

详见AnimatableLayer#draw方法

  //这里的layer来自于LottieDrawabe调用super.addLayers

        for (int i = 0; i < layers.size();i++) {

          layers.get(i).draw(canvas);

        }

通过查看源码得知:lottie源码中又这样一个东西 解析json数据 BaseKeyframeAnimation

BaseKeyframeAnimation从字面意思大概是用来描述关键帧的动画。做过动画的大概清楚“关键帧”的概念。BaseKeyframeAnimation的意义就是存放了一个动画中所有的关键帧,以及每一帧的过度进度。

Lottie Android 调研_第6张图片

在车机上性能问题:

动画加载时cpu 占用情况(json解析及动画初始化占用均值43%)

Lottie Android 调研_第7张图片

动画稳定后cpu 占比

解析完成后循环播放动画cpu占比约为15-20%之间

Lottie Android 调研_第8张图片

每一帧动画所占秒数为50-100ms之间,当然这是车机上应为设备硬件因素可能会有点慢

 

经过测试 加载一个352kb的json  动画 加载耗时5.4秒(不是毫秒)

做实验5次加载一个362kb 的json 文件 加载耗时分别为  5.13秒 、5.65秒、 5.62秒、5.40秒 4.89秒

Lottie Android 调研_第9张图片

内存占用情况:

Lottie Android 调研_第10张图片

VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)

PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

 

Lottie库的优缺点:

  优点:

1. 跨平台只有制作一套json动画文件便可以跨平台在 Android  ios ReactNeative上使用,lottie库负责解析json文件并播放动画

2. 可以支持网络下载json文件,本地播放,实时更新动画资源。

3. 运行时效率上仅仅用Canvas去draw而已,流畅度非常棒,所以哪怕在Listview里去大量显示,内存占用和绘图效率都远远高于帧动画。 

缺点:

1.在动画包含Mask时靠临时生成bitmap做的动画,性能有待提高。这部分貌似也有一些bug还有待解决。

2.通过Ae装插件bodyMovin把素材转换成.json兼容性不是很友好

3.最关键点在车车机上解析一个360kB的json文件在车机上耗时接近5秒,但是在我的mate9手机上可以瞬间解析完成并播放效果体验在手机上非常棒。

思考:

我们先从底层思考下如何在屏幕上绘制动画,最简单的方式是把动画分为多张图片,然后通过周期替换屏幕上绘制的图片来形成动画,这种暴力的方式非常简单,但缺点明显,很耗内存,动画播放中前后两张替换的图片在很多元素并没有变化,重复的内容浪费了空间。

为了提高空间利用率,可以把图片中的元素进行拆分,使用过photoshop的同学知道,其实在处理一张图片时,可以把一张复杂的图片使用多个图层来表示,每个图层上展示一部分内容,图层中的内容也可以拆分为多个元素。拆分元素之后,根据动画需求,可以单独对图层,甚至图层中的元素设置平移、旋转、收缩等动画。虽然说了那么多lottie的优点但是从在车机上以及和手机上效果体验性能以及json解析大文件速度上来看,lottie适合在硬件配置比较高的现代手机设备上但是不适合在硬件配置比较低的车载机上使用。


你可能感兴趣的:(Lottie Android 调研)