Kotlin告别FindViewById的原理

引子

使用Kotlin引用控件的时候,不用写findViewById,直接用id即可,比如

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_demo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.myapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tv_demo.text = "123"
    }
}

这样的代码异常清爽优雅,不用声明,不用findviewById.不过kotlin是怎么做到的呢,下面就一窥究竟

原理

最好的办法就是查看kt文件的字节码,然后再反编译下,android studio 有个功能能实现这个过程。
反编译
然后再点击Decompile 按钮,就完成了反编译,完整的代码如下

package com.example.myapplication;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R.id;
import java.util.HashMap;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"},
   d2 = {"Lcom/example/myapplication/MainActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}
)
public final class MainActivity extends AppCompatActivity {
   private HashMap _$_findViewCache;

   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(1300009);
      TextView var10000 = (TextView)this._$_findCachedViewById(id.tv_demo);
      Intrinsics.checkExpressionValueIsNotNull(var10000, "tv_demo");
      var10000.setText((CharSequence)"123");
   }

   public View _$_findCachedViewById(int var1) {
      if (this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(var1);
      if (var2 == null) {
         var2 = this.findViewById(var1);
         this._$_findViewCache.put(var1, var2);
      }

      return var2;
   }

   public void _$_clearFindViewByIdCache() {
      if (this._$_findViewCache != null) {
         this._$_findViewCache.clear();
      }

   }
}

相信看到这里大家就恍然大悟了,原来最后还是得通过findViewById来寻找空间,把这个过程封装一下,也就是findCachedViewById()方法的逻辑,然后实际的对象并不是tv_demo,而是var1000,用HashMap存储,如果其他地方也用tv_demo.xxxx()调用,tv_demo的控件实例对象是从_$_findViewCache获取的.反编译代码也写的比较鲁棒,可以看到这句代码 Intrinsics.checkExpressionValueIsNotNull(var10000, “tv_demo”);就是为了防止控件实例是空的情况,如果var10000是null的情况下,则直接抛出异常,一般情况下不会出现null.

总结

对使用了HashMap作为控件对象的存储器,表示有点遗憾,因为key正好是int,非常适合SparseArray,后面kotlin的版本也许更新这个问题.不过瑕不掩瑜,kotlin的这个操作让我有种解放思想的感觉,在原生时代,避免findViewById的方法差不多都会用Butterknife等注解框架,可是免去了调用findViewById,却要加注解,注解多了,也显的不优雅,变成了另外一种重复代码(样本代码).通过这个功能,我个人的想法kt文件只是一套规则,在这个规则之下看起来突破了以前java语法的束缚,比如tv_demo根本没有声明就能使用,但编译器编译的后的代码还是得遵循java 规范.kt文件生成的class文件还得运行在java虚拟机.目前短期内不会有变化.所以说,最好自己对kotlin的语法糖进行反编译看下,在java代码层是怎么实现的,知其然,知其所以然.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值