为了账号安全,请及时绑定邮箱和手机立即绑定

kotlin-android-extensions工作原理

标签:
Android

最近项目开始转kotlin其中用了官方推荐的kotlin-android-extensions,这次通过一次crash看kotlin-android-extensions免去findviewbyId的原理

1.一次crash的产生

引入kotlin-android-extensions这里不多做解释了,下面是一次fragment中不当使用kotlin-android-extensions导致crash的代码

 override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        rootView = inflater.inflate(R.layout.fragment_blank, container, false)
        //import kotlinx.android.synthetic.main.fragment_blank.*
       textView.text = "123"  //其中textView 是fragment_blank.xml中的一个TextView控件的id
        return rootView
    }

此时会存在以下错误

     Caused by: android.view.InflateException: Binary XML file line #0: Binary XML file line #0: Error inflating class fragment
                                                                        Caused by: android.view.InflateException: Binary XML file line #0: Error inflating class fragment
                                                                        Caused by: java.lang.IllegalStateException: textView must not be null
                                                                           at com.zjw.mykotlinproject.BlankFragment.onCreateView(BlankFragment.kt:45)
                                                                           at android.support.v4.app.Fragment.performCreateView(Fragment.java:2261)

好这个时候就有人说了fragment应该使用如下代码,可以解决这个异常

 override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        rootView = inflater.inflate(R.layout.fragment_blank, container, false)
        import kotlinx.android.synthetic.main.fragment_blank.view.*
      //  rootView?.textView?.text = "123"
        return rootView
    }

可以解决,但是这一种方式是一次回避查看源码的机会,因为以上2种方式的原理是不一样的,那么怎么分析上述的异常呢,因为代码被kotlin-android-extensions动了手脚,因此直接看是看不懂的,什么你要看kotlin-android-extensions源码?你自己找找吧,反正我在本地找到的source.jar是空的,网上github上也没找到,就算找到也要花时间分析,我只想知道我现在的这个fragment到底被kotlin-android-extensions改成了什么样子而已,emmmm~嘤嘤嘤~,怎么办呢?以下将给出办法

2. 使用kotlin代码还原术!让插件给我生成的代码在我面前一览无余吧!

第一步
图片描述
第二步
图片描述

按了 Decompile 按钮之后
图片描述

咦,onCreateView方法里面多了一个_$_findCachedViewById方法,在当前文件搜索这个方法可以看见实现如下图
图片描述

到这里就清楚了,kotlin-android-extensions让你直接用id就能得到xml中的控件对象并且使用,其实是他生成了findviewbyId的代码我们开发者就方便了,这里注意到这句this.getView()这里才是产生crash的真正原因,因为`this.getView()调用时候onCreateView还没有返回,因此最后findViewById的时候就产生了问题。要避免crash的话同时不想用 rootView?.textView``这一长串代码的话就这样干

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        textView.text = "123"
        // Log.e(TAG,TAG)
    }

好接着说 为什么下面代码使用正常

   override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        rootView = inflater.inflate(R.layout.fragment_blank, container, false)
        //import kotlinx.android.synthetic.main.fragment_blank.*
        //textView.text = "123"
        // import kotlinx.android.synthetic.main.fragment_blank.view.*
        rootView?.textView?.text = "123"
        return rootView
    }

来继续反编译操作!看看真相
图片描述
简单吧

     // import kotlinx.android.synthetic.main.fragment_blank.view.*
        rootView?.textView?.text = "123"

上述代码做的事情就是使用了rootView然后用rootView.findViewById 所以我上文才说这一种方式是一次回避查看源码的机会

3.思维扩展

通过反编译手段我们除了可以看见第三方插件对源码的改动以外,还能定位到java和kotlin之间如何相互翻译语义的,有的时候语义翻译不对会导致crash(具体例子有兴趣就加我和我讨论吧),当出现神奇的Bug的时候建议你往这方面想想

点击查看更多内容
6人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
1万
获赞与收藏
137

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消