最近项目开始转kotlin其中用了官方推荐的kotlin-android-extensions,此次经过一次crash看kotlin-android-extensions免去findviewbyId的原理java
引入kotlin-android-extensions
这里很少作解释了,下面是一次fragment中不当使用kotlin-android-extensions
致使crash的代码android
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
}
复制代码
此时会存在如下错误git
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应该使用以下代码,能够解决这个异常github
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~嘤嘤嘤~,怎么办呢?如下将给出办法bash
第一步 app
按了 Decompile 按钮以后 ide
咦,onCreateView
方法里面多了一个_$_findCachedViewById
方法,在当前文件搜索这个方法能够看见实现以下图 ui
到这里就清楚了,kotlin-android-extensions
让你直接用id就能获得xml中的控件对象而且使用,实际上是他生成了findviewbyId的代码咱们开发者就方便了,这里注意到这句this.getView()
这里才是产生crash的真正缘由,由于`this.getView()调用时候
onCreateView尚未返回,所以最后findViewById的时候就产生了问题。要避免crash的话同时不想用
rootView?.textView``这一长串代码的话就这样干this
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView.text = "123"
// Log.e(TAG,TAG)
}
复制代码
好接着说 为何下面代码使用正常spa
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 因此我上文才说这一种方式是一次回避查看源码的机会
经过反编译手段咱们除了能够看见第三方插件对源码的改动之外,还能定位到java和kotlin之间如何相互翻译语义的,有的时候语义翻译不对会致使crash(具体例子有兴趣就加我和我讨论吧),当出现神奇的Bug的时候建议你往这方面想一想