最近項目開始轉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的時候建議你往這方面想一想