如何检测 Android 片段内扩展的空指针异常的原因
How to detect cause of Null Pointer Exception on Android Extensions inside fragments
在我的 android 应用程序中,我使用 Kotlin Android Extensions
从片段和活动中的 XML 布局访问视图。这在几乎所有情况下都适用,但当实际用户使用应用程序时,我不断收到关于 crashlytics 的空指针崩溃报告。我无法重现此问题或无法理解此问题的原因。
崩溃发生在具有 Kotlin Android Extensions
用法的片段上。
下面是我的代码:
Kotlin 文件
import kotlinx.android.synthetic.main.fragment_documents.*
@Keep
class BottomNavFragment : Fragment() {
private val activityViewModel: MainActivityViewModel by activityViewModels()
lateinit var mainActivity: MainActivity
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val rootView = inflater.inflate(R.layout.fragment_documents, container, false)
mainActivity = activity as MainActivity
return rootView
}
companion object {
@JvmStatic
fun newInstance() =
BottomNavFragment().apply {
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
setUpTabLayout()
handleViewModelObservers()
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun handleViewModelObservers() {
activityViewModel.isBackToHomeEnabled.observe(viewLifecycleOwner,
{ isHomeEnabled ->
if (isHomeEnabled!!) {
tabLayout.getTabAt(1)?.select()
}
})
}
private fun setUpTabLayout() {
try {
if (mainActivity != null && activityViewModel != null && viewPager != null &&tabLayout!=null) {
val adapter = FilesViewPagerAdapter(childFragmentManager)
adapter.addFragments()
viewPager.adapter = adapter
viewPager.offscreenPageLimit = 4
viewPager.currentItem = 1
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
mainActivity.clearSearch()
}
override fun onPageScrollStateChanged(state: Int) {
}
})
tabLayout.setupWithViewPager(viewPager)
tabLayout.getTabAt(0)?.icon = mainActivity.getDrawable(R.drawable.test1)
tabLayout.getTabAt(1)?.icon = mainActivity.getDrawable(R.drawable.test2)
tabLayout.getTabAt(2)?.icon = mainActivity.getDrawable(R.drawable.test3)
tabLayout.getTabAt(3)?.icon = mainActivity.getDrawable(R.drawable.test3)
tabLayout.getTabAt(4)?.icon = mainActivity.getDrawable(R.drawable.test5)
tabLayout.getTabAt(0)?.text = getString(R.string.test1)
tabLayout.getTabAt(1)?.text = getString(R.string.test2)
tabLayout.getTabAt(2)?.text = getString(R.string.test3)
tabLayout.getTabAt(3)?.text = getString(R.string.test4)
tabLayout.getTabAt(4)?.text = getString(R.string.test5)
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
XML布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".views.fragments.BottomNavFragment">
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="@dimen/_50sdp"
android:background="@color/colorPrimary" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/_60sdp"
android:layout_marginStart="@dimen/_10sdp"
android:layout_marginTop="@dimen/_20sdp"
android:layout_marginEnd="@dimen/_10sdp"
android:background="@drawable/bg_round_view_white"
app:tabSelectedTextColor="@color/colorPrimary"
app:tabTextAppearance="@style/ThemeTextAppearanceTab">
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tabLayout" />
</RelativeLayout>
崩溃报告
Fatal Exception: java.lang.NullPointerException: viewPager must not be null
at mypackagename.views.fragments.BottomNavFragment$setUpTabLayout.run(BottomNavFragment.java)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6946)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
到目前为止我学到了什么:
Kotlin Android 扩展实际上有 findViewById
、 findViewCache
和 clearFindViewByIdCache
实现。
正如本 article
中所述
The problem with fragments is that the view can be recreated but the
fragment instance will be kept alive. What happens then? This means
that the views inside the cache would be no longer valid.
那么,我怎么知道视图不再有效?或者,我可以做些什么来防止这次崩溃?
任何帮助将不胜感激。
Android 扩展插件已弃用,官方提议使用 ViewBinding
代替,它提供编译时访问 xml 布局的视图,而无需执行 viewFindById
在您的每个视图上。
This 此处的页面将为您提供设置代码以使用视图绑定的步骤,确保检查片段上的代码片段以清除您对视图的引用
在我的 android 应用程序中,我使用 Kotlin Android Extensions
从片段和活动中的 XML 布局访问视图。这在几乎所有情况下都适用,但当实际用户使用应用程序时,我不断收到关于 crashlytics 的空指针崩溃报告。我无法重现此问题或无法理解此问题的原因。
崩溃发生在具有 Kotlin Android Extensions
用法的片段上。
下面是我的代码:
Kotlin 文件
import kotlinx.android.synthetic.main.fragment_documents.*
@Keep
class BottomNavFragment : Fragment() {
private val activityViewModel: MainActivityViewModel by activityViewModels()
lateinit var mainActivity: MainActivity
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val rootView = inflater.inflate(R.layout.fragment_documents, container, false)
mainActivity = activity as MainActivity
return rootView
}
companion object {
@JvmStatic
fun newInstance() =
BottomNavFragment().apply {
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
setUpTabLayout()
handleViewModelObservers()
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun handleViewModelObservers() {
activityViewModel.isBackToHomeEnabled.observe(viewLifecycleOwner,
{ isHomeEnabled ->
if (isHomeEnabled!!) {
tabLayout.getTabAt(1)?.select()
}
})
}
private fun setUpTabLayout() {
try {
if (mainActivity != null && activityViewModel != null && viewPager != null &&tabLayout!=null) {
val adapter = FilesViewPagerAdapter(childFragmentManager)
adapter.addFragments()
viewPager.adapter = adapter
viewPager.offscreenPageLimit = 4
viewPager.currentItem = 1
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
mainActivity.clearSearch()
}
override fun onPageScrollStateChanged(state: Int) {
}
})
tabLayout.setupWithViewPager(viewPager)
tabLayout.getTabAt(0)?.icon = mainActivity.getDrawable(R.drawable.test1)
tabLayout.getTabAt(1)?.icon = mainActivity.getDrawable(R.drawable.test2)
tabLayout.getTabAt(2)?.icon = mainActivity.getDrawable(R.drawable.test3)
tabLayout.getTabAt(3)?.icon = mainActivity.getDrawable(R.drawable.test3)
tabLayout.getTabAt(4)?.icon = mainActivity.getDrawable(R.drawable.test5)
tabLayout.getTabAt(0)?.text = getString(R.string.test1)
tabLayout.getTabAt(1)?.text = getString(R.string.test2)
tabLayout.getTabAt(2)?.text = getString(R.string.test3)
tabLayout.getTabAt(3)?.text = getString(R.string.test4)
tabLayout.getTabAt(4)?.text = getString(R.string.test5)
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
XML布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".views.fragments.BottomNavFragment">
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="@dimen/_50sdp"
android:background="@color/colorPrimary" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/_60sdp"
android:layout_marginStart="@dimen/_10sdp"
android:layout_marginTop="@dimen/_20sdp"
android:layout_marginEnd="@dimen/_10sdp"
android:background="@drawable/bg_round_view_white"
app:tabSelectedTextColor="@color/colorPrimary"
app:tabTextAppearance="@style/ThemeTextAppearanceTab">
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tabLayout" />
</RelativeLayout>
崩溃报告
Fatal Exception: java.lang.NullPointerException: viewPager must not be null
at mypackagename.views.fragments.BottomNavFragment$setUpTabLayout.run(BottomNavFragment.java)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6946)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
到目前为止我学到了什么:
Kotlin Android 扩展实际上有 findViewById
、 findViewCache
和 clearFindViewByIdCache
实现。
正如本 article
The problem with fragments is that the view can be recreated but the fragment instance will be kept alive. What happens then? This means that the views inside the cache would be no longer valid.
那么,我怎么知道视图不再有效?或者,我可以做些什么来防止这次崩溃?
任何帮助将不胜感激。
Android 扩展插件已弃用,官方提议使用 ViewBinding
代替,它提供编译时访问 xml 布局的视图,而无需执行 viewFindById
在您的每个视图上。
This 此处的页面将为您提供设置代码以使用视图绑定的步骤,确保检查片段上的代码片段以清除您对视图的引用