You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Play Store上报的MainFragment空指针异常问题

问题:Play Store上报空指针异常,本地设备无问题,请求排查原因

我的应用在Play Store收到如下空指针异常堆栈信息,无法定位异常原因,而我的摩托罗拉G31(Android SDK 32)设备上运行一切正常,且该异常在各类设备均出现,并非SDK或设备特定问题。

异常堆栈信息

Exception java.lang.NullPointerException:
  at in.vikalp.gridforart.ui.MainFragment$onCreateView$1.invoke (MainFragment.java:192)
  at in.vikalp.gridforart.ui.MainFragment$onCreateView$1.invoke (MainFragment.java:192)
  at in.vikalp.gridforart.ui.MainFragment$sam$androidx_lifecycle_Observer$0.onChanged (MainFragment.java)
  at androidx.lifecycle.LiveData.considerNotify (LiveData.java)
  at androidx.lifecycle.LiveData.dispatchingValue (LiveData.java:56)
  at androidx.lifecycle.LiveData.setValue (LiveData.java)
  at androidx.lifecycle.MutableLiveData.setValue (MutableLiveData.java)
  at androidx.lifecycle.LiveDataScopeImpl$emit$2.invokeSuspend (LiveDataScopeImpl.java:44)
  at androidx.lifecycle.LiveDataScopeImpl$emit$2.invoke (LiveDataScopeImpl.java)
  at androidx.lifecycle.LiveDataScopeImpl$emit$2.invoke (LiveDataScopeImpl.java)
  at kotlin.coroutines.jvm.internal.DebugProbesKt.j0 (DebugProbes.kt:5)
  at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext (BuildersKt__Builders_common.kt:91)
  at kotlinx.coroutines.BuildersKt.withContext (Builders.kt:91)
  at androidx.lifecycle.LiveDataScopeImpl.emit (LiveDataScopeImpl.java)
  at androidx.lifecycle.FlowLiveDataConversions$asLiveData$1$1.emit (FlowLiveDataConversions.java)
  at in.vikalp.gridforart.datastoreRepo.DatastoreRepository$special$$inlined$map$1$2.emit (DatastoreRepository.java:270)

涉及的MainFragment代码

@AndroidEntryPoint
class MainFragment : Fragment(), EasyPermissions.PermissionCallbacks {
        
 override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = this
        binding.mainViewModel = mainViewModel

        gridSettingsViewModels.readGridSettings.observe(viewLifecycleOwner) {
            Log.e(TAG, "onCreateView: $it")
            mainViewModel.setSettings(it)
            if (oldImageUri.toString() != EMPTY_URI)
                mainViewModel.createBitmap(oldImageUri!!)
        }

        mainViewModel.imageUriLiveData.observe(viewLifecycleOwner) {
            Log.e(TAG, "onCreateView: $it")
            oldImageUri = Uri.parse(it)
        }

        binding.gridButton.setOnClickListener {
            findNavController().navigate(MainFragmentDirections.actionMainFragmentToGridBottomSheet())
        }

        binding.filterButton.setOnClickListener {
            findNavController().navigate(MainFragmentDirections.actionMainFragmentToFilterDialogFragment())
        }

        binding.tapBarMenu.setOnClickListener {
            binding.tapBarMenu.toggle()
        }

        binding.galleryButton.setOnClickListener {
            getImageFromGallery()
        }

        binding.downloadButton.setOnClickListener {
            mainViewModel.liveBitmapReady.observeOnce(viewLifecycleOwner){
                savedImageBitmap(it)
            }
        }


        return binding.root
    }
    }
  private fun getImageFromGallery() {
        if (PermissionUtils.hasCameraPermission(requireContext())) {
            getImage.launch("image/*")
        } else {
            requestPermissions()
        }
    }

    private fun requestPermissions() {
        EasyPermissions.requestPermissions(
            this,
            "You need to accept storage permission to use this app",
            REQUEST_CODE_CAMERA_PERMISSIONS,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
        )
    }

    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {

    }

// 标注:此处为代码中的192行位置
    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            AppSettingsDialog.Builder(this).build().show()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
    }

排查分析

从堆栈信息看,空指针异常实际发生在MainFragment.onCreateView中的第一个LiveData Observer回调里(编译后代码对应192行,即gridSettingsViewModels.readGridSettings.observe的lambda内部,而非你标注的权限方法行)。结合代码细节,主要原因及解决方式如下:

1. oldImageUri为空触发空指针

你的代码中:

if (oldImageUri.toString() != EMPTY_URI)
    mainViewModel.createBitmap(oldImageUri!!)

oldImageUri应为Fragment内的可空变量(初始值为null),当gridSettingsViewModels.readGridSettings的回调先于mainViewModel.imageUriLiveData触发时,oldImageUri还未被赋值,此时调用oldImageUri.toString()会直接抛出空指针,后续的oldImageUri!!强制非空也会触发异常。

解决方法:

  • 增加空判断,避免直接调用可空变量的方法:
    if (oldImageUri != null && oldImageUri.toString() != EMPTY_URI) {
        mainViewModel.createBitmap(oldImageUri)
    }
    
  • 或使用安全调用简化逻辑:
    oldImageUri?.takeIf { it.toString() != EMPTY_URI }?.let {
        mainViewModel.createBitmap(it)
    }
    
  • 给oldImageUri设置初始值,避免为空:
    private var oldImageUri: Uri = Uri.parse(EMPTY_URI)
    

2. 验证ViewModel注入有效性(概率较低)

虽然使用了@AndroidEntryPoint,但如果Hilt注入的gridSettingsViewModels或mainViewModel为空,也会导致回调中的空指针。这种情况通常会在Fragment初始化时崩溃,可通过日志验证:

// 在onCreateView开头添加
Log.d(TAG, "gridSettingsViewModels: $gridSettingsViewModels, mainViewModel: $mainViewModel")

同时检查Hilt配置,确保ViewModel被正确绑定到Fragment。

3. 调整LiveData观察时机

gridSettingsViewModels.readGridSettings可能在Fragment视图未完全初始化时触发回调,可将观察逻辑移至onViewCreated中,确保所有变量和View都已准备完毕:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    gridSettingsViewModels.readGridSettings.observe(viewLifecycleOwner) {
        // 原逻辑
    }
    mainViewModel.imageUriLiveData.observe(viewLifecycleOwner) {
        // 原逻辑
    }
}

内容的提问来源于stack exchange,提问作者Vikalp Ranjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 18:44:55