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

Xamarin Forms导出ApplicationData下SQLite库到SD卡报空引用异常

问题排查结论

异常出在WriteAllBytes代码行,实际触发原因按出现概率从高到低排列如下:

  • 硬编码/sdcard/路径无效:Android系统没有统一固定的外部存储根路径,不同厂商、不同系统版本的外部存储路径可能为/storage/emulated/0/、/mnt/sdcard/等,硬编码路径会导致程序找不到目标目录,写入时抛出异常。同时Android 4.4之后普通应用无权直接访问系统根级目录,直接写/sdcard/属于非法路径访问。
  • 缺少存储权限:Android 6.0以上版本写入外部存储需要动态申请危险权限,仅在清单文件声明权限不做动态申请的话,应用没有对应路径的写入权限,调用文件写入方法会触发异常。
  • 入参为空:如果源数据库路径拼接错误(sqlite-net默认创建的数据库一般带.db3/.db后缀,原代码拼接的文件名是Mydatabase没有后缀,大概率读不到真实文件),ReadAllBytes返回null,传入WriteAllBytes时就会触发空引用异常。
修复步骤
  1. 补全权限配置
    在Android项目的AndroidManifest.xml中添加必要权限:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    针对Android 10及以上版本,在<application>节点添加分区存储兼容配置:
    <application
      android:requestLegacyExternalStorage="true"
      ...其余配置>
    
  2. 替换硬编码路径,增加权限申请、文件校验逻辑
    不要手动写/sdcard/,通过系统官方API获取合法的外部存储路径;执行复制前先校验权限、校验源文件是否存在,避免空值传入写入方法。
  3. 修复后的可运行代码:
private const int StorageRequestCode = 1001;
private void CopyDBButton_Clicked(object sender, EventArgs e)
{
    // 未获取存储权限时先发起权限申请
    if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
    {
        ActivityCompat.RequestPermissions(this, 
            new[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 
            StorageRequestCode);
        return;
    }

    // 注意核对sqlite数据库实际文件名,补充对应后缀,避免找不到源文件
    var basePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
    var finalPath = Path.Combine(basePath, "Mydatabase.db3");
    
    if (!File.Exists(finalPath))
    {
        Toast.MakeText(this, "数据库文件不存在,导出失败", ToastLength.Short).Show();
        return;
    }
    CopyDatabase(finalPath);
}

public static void CopyDatabase(string databasePath)
{
    var bytes = File.ReadAllBytes(databasePath);
    if (bytes == null || bytes.Length == 0)
    {
        Toast.MakeText(Application.Context, "读取数据库内容为空", ToastLength.Short).Show();
        return;
    }
    // 通过系统API获取外部存储根路径,禁止硬编码
    var externalPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
    var fileCopyName = Path.Combine(externalPath, 
        string.Format("Database_{0:dd-MM-yyyy_HH-mm-ss-tt}.db", DateTime.Now));
    File.WriteAllBytes(fileCopyName, bytes);
    Toast.MakeText(Application.Context, $"数据库已导出至:{fileCopyName}", ToastLength.Long).Show();
}
额外注意
  • 执行复制操作前,先关闭所有打开的SqliteConnection连接,避免读取到处于写入中间态的损坏文件。
  • 权限申请的回调逻辑需要在对应Activity中实现,用户拒绝授权时要做提示,不要直接执行复制逻辑。

内容的提问来源于stack exchange,提问作者Adrian Hurst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:09:21