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

Google Play预启动报告检测到.NET MAUI应用Java空引用异常

.NET MAUI应用Google Play预启动报告空引用异常排查与解决

我用.NET MAUI开发的应用在Debug和Release模式下运行均无异常,但上传至Google Play后,Google Play Console预启动报告检测到一处Java层空引用异常,导致应用无法通过审核。

错误日志

at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
Exception android.runtime.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object
   at WakeUp.CreateAlarmPage.OnButtonClicked(Object sender, EventArgs e)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0()
   at Java.Lang.Thread.RunnableImplementor.Run()
   at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
  at mono.java.lang.RunnableImplementor.n_run
  at mono.java.lang.RunnableImplementor.run (RunnableImplementor.java:31)
  at android.os.Handler.handleCallback (Handler.java:942)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:201)
  at android.os.Looper.loop (Looper.java:288)
  at android.app.ActivityThread.main (ActivityThread.java:7918)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)

相关代码

private async void OnButtonClicked(object sender, EventArgs e)
{
    PermissionStatus check;
    if (Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Tiramisu)
    {
        check = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
        if (check != PermissionStatus.Granted)
        {
            PermissionStatus request = await Permissions.RequestAsync<Permissions.StorageRead>();
            if (request != PermissionStatus.Granted)
            {
                return;
            }
        }
    }

    var customFileType = new FilePickerFileType(
        new Dictionary<DevicePlatform, IEnumerable<string>>
        {
            { DevicePlatform.Android, new[] { "audio/aac", "audio/aacp", "audio/webm", 
"audio/x-mp4", "audio/x-m4a", "audio/mp4", "audio/m4a",
                "audio/wav", "audio/mpeg", "audio/ogg", "audio/x-flac" } },
        }
        );
    PickOptions options = new()
    {
        FileTypes = customFileType,
    };
    var result = await FilePicker.Default.PickAsync(options);
    if (result != null)
    {
        Debug.WriteLine("FullPath: " + result.FullPath);
        mediaPlayer.Source = result.FullPath;
        _soundPath = result.FullPath;
        soundName.Text = result.FileName;
    }
}

异常原因分析

从错误栈和代码来看,空引用异常大概率出在OnButtonClicked方法内的以下几个点:

  • mediaPlayer对象未完成初始化:页面加载时若未给mediaPlayer赋值,设置Source属性时会触发空引用。
  • soundName控件未正确绑定:XAML中x:Name="soundName"的控件未与后台代码正确关联,访问Text属性时为空。
  • FilePicker.Default极端情况下返回null:尽管官方文档说明默认实例不会为空,但预启动测试的特殊设备环境可能导致此情况。

解决方案

1. 确保mediaPlayer完成初始化

在页面的构造函数或OnAppearing方法中添加初始化逻辑,避免使用时为空:

protected override void OnAppearing()
{
    base.OnAppearing();
    mediaPlayer ??= new MediaPlayer(); // 根据实际业务逻辑调整初始化方式
}

2. 验证soundName控件绑定

  • 检查XAML中对应的控件是否设置了正确的x:Name="soundName";
  • 清理解决方案并重新编译,确保XAML代码生成正常,后台能正确引用该控件。

3. 增加FilePicker.Default空判断

在调用PickAsync前先校验实例是否存在:

if (FilePicker.Default == null)
{
    // 可添加用户提示,告知无法使用文件选择功能
    return;
}
var result = await FilePicker.Default.PickAsync(options);

4. 全局空引用防护

对所有可能为空的对象使用空传播运算符?.,避免直接访问属性:

if (result != null)
{
    Debug.WriteLine("FullPath: " + result.FullPath);
    mediaPlayer?.Source = result.FullPath;
    _soundPath = result.FullPath;
    soundName?.Text = result.FileName;
}

额外建议

模拟预启动测试的边界场景,比如权限被拒绝、取消文件选择等,验证代码鲁棒性,确保覆盖所有异常分支。

内容的提问来源于stack exchange,提问作者Metehan Ö.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:07:36