使用Anko创建快400%的布局

pwul2413 7年前
   <p>我用Anko已经有了一段时间了,很好奇Anko提供了哪些优势。所以做了一些性能测试。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/105e71d26f7a05424c73f7bc6dc464d8.png"></p>    <p>我打算把一个比较高级的布局迁移到Anko。这个布局包含了一个RelativeLayout,里面有17个ImageView(全是用的.9图片),一个SurfaceView作为取景器,两个普通的TextView。仍然是一个比较干净的布局,因为没有嵌套。</p>    <h3><strong>为什么Anko性能更优?</strong></h3>    <p>XML布局是在运行时解析的。也就是说XML需要从资源文件中获取,然后XmlPullParser需要解析所有的元素并一个一个的创建它们。还要解析元素的属性,然后设置。这个过程非常繁重,那么到底浪费了多长时间在上面呢?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d5a23807ff4140a1eda1c4486b7d50e4.png"></p>    <p>我在4个老旧的设备上运行了测试,虽然老但又是每个安卓开发者都需要处理的设备。我们在所有的设备上运行了大约4次 <a href="/misc/goto?guid=4959726541602414226" rel="nofollow,noindex">DevMetrics</a> 。结果发现性能差距居然达到了350%-600%的程度。</p>    <p>随着对设计/动画以及性能的需求日益增加,我认为大家都对应用的速度非常关心,所以为什么要使用Anko就不用多说了吧。</p>    <p>除了布局变快了之外,我们现在构建布局是在运行时,所以可以加上一些逻辑判断。让我们用Anko创建一个master-detail的示例:</p>    <pre>  <code class="language-java">class MainActivity : AppCompatActivity() {    private var toolBar: Toolbar? = null    private var container: ViewGroup? = null      override fun onCreate(savedInstanceState: Bundle?) {      super.onCreate(savedInstanceState)      coordinatorLayout {        fitsSystemWindows = true          appBarLayout {          toolBar = toolbar {            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f          }.lparams(width = matchParent, height = actionBarSize())                }.lparams(width = matchParent)          container = frameLayout()          .lparams(width = matchParent, height = matchParent) {            behavior = AppBarLayout.ScrollingViewBehavior()          }      }    }  }</code></pre>    <p>就如你看到的那样,用Anko写布局跟xml没有什么两样。除了布局构建上的优势之外,我们可以做兼容性检查,根据OS的版本设置elevation,而不是在布局目录中写两个xml。</p>    <p>对于提取MainActivity中的的元素,Anko提供了AnkoComponent来解决这个问题,但是你还是需要使用findViewById和类型转换。不过这也是可以轻松解决的。</p>    <p>我们创建一个提供了一个bind 和一个unbind方法的接口。类似于使用Butter Knife来绑定与解绑。</p>    <pre>  <code class="language-java">interface ViewBinder<in T> {      fun bind(t: T) : View      fun unbind(t: T)  }</code></pre>    <p>现在我们可以轻松的从前面的布局中提取出元素到MainLayout.kt class。</p>    <pre>  <code class="language-java">class MainLayout : ViewBinder<MainActivity> {      override fun bind(mainActivity: MainActivity): View =      mainActivity.UI {        coordinatorLayout {          fitsSystemWindows = true            appBarLayout {            mainActivity.toolBar = toolbar {              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f            }.lparams(width = matchParent, height = actionBarSize())                  }.lparams(width = matchParent)            mainActivity.container = frameLayout()            .lparams(width = matchParent, height = matchParent) {              behavior = AppBarLayout.ScrollingViewBehavior()            }        }      }.view      override fun unbind(mainActivity: MainActivity) {      mainActivity.container = null      mainActivity.recycView = null    }  }</code></pre>    <p> </p>    <pre>  <code class="language-java">public class MainActivity extends AppCompatActivity {        LinearLayout container;      RecyclerView recycView;      FrameLayout detailContainer;            private MainLayout mainLayout = new MainLayout();        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(mainLayout.bind(this));          ...      }  }</code></pre>    <p>现在我们的activity看起来是不是干净多了呢?除了调用了setContentView(mainLayout.bind(this))之外没有什么特别之处,它将设置content view并把view和变量绑定。就如你看到的那样,无需findViewById和类型转换。</p>    <h3><strong>Runtime layouts</strong></h3>    <p>如果你想在构建布局的时候加入逻辑,你肯定会喜欢这个东西。</p>    <pre>  <code class="language-java">configuration(orientation = Orientation.LANDSCAPE, smallestWidth = 700) {    recyclerView {      init()    }.lparams(width = widthProcent(50), height = matchParent)        frameLayout().lparams(width = matchParent, height = matchParent)  }    fun <T : View> T.widthProcent(procent: Int): Int =    getAppUseableScreenSize().x.toFloat().times(procent.toFloat() / 100).toInt()</code></pre>    <p>让我们分析一下上面的代码,anko提供了一个语法友好的configuration用于在运行时检查配置。可以检查screenSize, density, language, orientation, fromSdk, sdk, uiMode, nightMode, rightToLeft 以及 smallestWidth。因此这里的layout DSL只有在设备为横向、横向宽度是700的时候才会运行。</p>    <p>我们还可以轻松计算出大小,这里将在运行时计算出屏幕宽度并把内容宽度设置为屏幕宽度的50% 。</p>    <h3><strong>总结</strong></h3>    <p>Anko提供了几种解决传统xml布局构建方式的办法。它绕开了xml布局方式的所有开销。你无需处理findViewById或者类型转换,而且在运行时构建布局可以让你添加想要的逻辑,让布局更动态。所有这些只需要花很小的代价</p>    <p> </p>    <p> </p>    <p>来自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/1123/6795.html</p>    <p> </p>