zoukankan      html  css  js  c++  java
  • Xamarin.Forms 复制本地SQLite数据库

     

    Xamarin.Forms 复制本地SQLite数据库

     

            2022年元旦的三天假期,闲暇无事。

           想到一个多月以前浏览博客园收藏的两则新闻。一是2021年11月9日 Microsoft 发布了迄今为止最快的 .NET 版本---- .NET6 ,二是号称宇宙最强IDE的Visual Studio 2022 正式版也于同日正式发布。一时技痒,就想用Xamarin.Forms来做一个移动端应用程序。

    引子:关于年过半百的老李头

           何谓一时技痒呢?本人是一名年过半百(1971年出生)的编程爱好者,92年左右开始接触电脑,当时的电脑叫286或386,内存大多是32K或者64K。存储介质是一种叫软盘的东西,现在的计算机为什么从C盘开始,好像就是因为当年的A盘和B盘都是软盘驱动器,两个软盘驱动器一大一小,小的三寸盘1.44M,大的五寸盘容量512K。当年最高光的时刻就是用BASIC语言,使用一种叫DBASIC III的工具编写了一套可打印的单位工资表。

           90年代中期开始接触编程,使用Pascal语言、Delphi3和SQL Server6.5数据库来开发Windows桌面应用程序,曾经获得过省部级的科技成果三等奖。到了20年代初期,ASP等脚本语言逐渐流行起来,开发方式也分为C/S方式和B/S方式。谁也想不到当年毫不起眼的JavaScript和mysql等今天能如此流行。Borland公司也在和Microsoft公司的大战中逐渐失利,最主要的是由于一些个人原因就没有再跟进学习。一晃二十年过去了。当年读过一本书叫《Borland传奇》记录了好多当年的技术大牛传奇故事,不知今天的开发者又有多少人读过。以史为鉴,谁会是今天的Borland?谁又会是明天的Microsoft?三十年后,谁在河东?谁在河西?这,也许是程序员的一种悲哀。

            为什么要做移动端应用?由于从来没有做过网页开发,不会任何前端开发语言,一直听说微软有一个宏大的理想,要用.NET一统江湖,对我而言,就是能用C#写网页,也就一直比较关注。不久前,微软推出一个新的 UI 框架Blazor ,知乎上说:“blazor是基于C#的,主要的开发工具是C#(无需使用Javascript), 这对于.net 程序员来说非常友好,可以无需为了前端学习各种不同的技术,同时基于C#的代码也可以很好的和原有的Javascript代码进行沟通。”实验了一下,纷繁复杂的各种前端技术还是给我整蒙了。想到也许今天的网站开发也许就是二十年前的WinForm,于是就想直接挑战一下跨平台移动端应用开发。

            从零开始,Hello World。

     

    问题:如何复制本地数据库到目标环境

            如果您向我一样,是一个零基础移动端的开发者,那么按照Xamarin.Forms 快速入门(链接地址)的示例指引,那么应该在30分钟左右,就会生成第一个Xamarin的应用,您也可以直接下载示例(链接地址)。链接数据库和创建数据表的方法如下,挺简单吧?

            public NoteDatabase(string dbPath)
            {
                database = new SQLiteAsyncConnection(dbPath);
                database.CreateTableAsync<Note>().Wait();
            }

     

            但我希望App可以提供查询功能,查询我已经准备好的数据库中的数据。于是对于我这样零基础初学者的难题来了:

            --------尝试将本地数据库直接复制到虚拟机中,把查询的功能做完。按照传统的WinForm思维,要直接复制数据库到目标环境中中,需要先知道虚拟机中创建数据库的位置。于是安装了Android的SDK,但没有在android目录中找到DDMS工具。各种ADB shell 在控制台中敲了若干命令行,还是没有在/data/user/0/com.companyname.notes/files/目录下看到App创建的Notes.db3数据库。最终明白了所谓应用沙盒,应用程序可以使用的专有区域。在默认情况下,除操作系统本身外,其他任何应用程序都无法访问此区域。于是放弃。

            --------尝试将数据直接INSERT到已经创建好的数据库中。写一个数据库初始化类,直接将初始化的数据Insert进去。少量数据这也许是一个折中的方法,但对于近万条的初始数据,即使能Insert,程序启动时也不能等这么久啊。于是放弃。

            以上两个尝试消耗了三天假期中的两天。

            继续查找微软的Xamarin文档,在“Xamarin.Forms 本地数据库”(链接地址)页面中写道:在多种情况下,可能需要复制 SQLite 数据库:数据库随应用程序一起提供,但必须复制或移动到移动设备上的可写存储。说的挺明白,但没找到后续操作的明确指引。

    方法:通过文件流的方式来完成复制操作

           在阅读了大量博客园里的文章之后(其中大部分是在第一次尝试直接复制数据库到Android虚拟机中时阅读的),看到denniswang 博主在2011年10月18日《Android中asset文件夹和raw文件夹区别》(链接地址)文章中提到的思路。

            虽然感觉这篇十年前的文章应该是采用DSK的原生Android开发,但有两点明确的启示:一是Android中Resource/Raw下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。二是读取Resource/Raw下的文件资源,通过获取输入流来的方式来进行写操作。曙光初现,假期还有一天,继续挑战,最终具体步骤如下:

            一、准备本地SQLite 数据库文件。首先在Notes.Android项目的Resources目录下新建文件夹raw,然后分别将本机包含初始数据的Notes.db3在VS中复制到Notes.Android项目下的raw和Notes.iOS项目的Resources目录下。然后重新生成解决方案,让复制到项目中的资源可编译。

     

            二、新建ISQLite 接口并分别实现。1、在Notes项目下新建类ISQLite.cs。

    using SQLite;
    
    namespace Notes
    {
        public interface ISQLite
        {
            SQLiteAsyncConnection GetConnection();
        }
    }

             2、在Notes.Android项目下打开MainActivity.cs。添加属性如下:internal static MainActivity Instance { get; private set; }

            3、在Notes.Android项目下新建类SQLite_Android.cs

    using System;
    using Notes.Droid;
    using Xamarin.Forms;
    using System.IO;
    using SQLite;
    
    [assembly: Dependency(typeof(SQLite_Android))]
    
    namespace Notes.Droid
    {
        public class SQLite_Android : ISQLite
        { 
            public SQLiteAsyncConnection GetConnection()
            {
                var sqliteFilename = "Notes.db3";
                string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); 
                var path = Path.Combine(documentsPath, sqliteFilename);
                Console.WriteLine(path);
                if (!File.Exists(path))
                {
                    var readStream = MainActivity.Instance.Resources.OpenRawResource(Resource.Raw.Notes);  
                    FileStream writeStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
                    int Length = 256;
                    Byte[] buffer = new Byte[Length];
                    int bytesRead = readStream.Read(buffer, 0, Length);
                    while (bytesRead > 0)
                    {
                        writeStream.Write(buffer, 0, bytesRead);
                        bytesRead = readStream.Read(buffer, 0, Length);
                    }
                    readStream.Close();
                    writeStream.Close();
                }
                var conn = new SQLiteAsyncConnection(path);
                return conn;
            }
        }
    }

             4、在Notes.iOS项目下新建类SQLite_iOS.cs

    using System;
    using Notes.iOS;
    using Xamarin.Forms;
    using System.IO;
    using SQLite;
    
    [assembly: Dependency(typeof(SQLite_iOS))]
    
    namespace Notes.iOS
    {
        public class SQLite_iOS : ISQLite
        {
            public SQLite_iOS()
            {
            }
            public SQLiteAsyncConnection GetConnection()
            {
                var sqliteFilename = "Notes.db3";
                string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); 
                string libraryPath = Path.Combine(documentsPath, "..", "Library"); 
                var path = Path.Combine(libraryPath, sqliteFilename);
    
                // This is where we copy in the prepopulated database
                Console.WriteLine(path);
                if (!File.Exists(path))
                {
                    File.Copy(sqliteFilename, path);
                }
                var conn = new SQLiteAsyncConnection(path);
                return conn;
            }
        }
    }

            三、修改 Notes项目下NoteDatabase.cs类的构造函数public NoteDatabase(string dbPath)。

            readonly SQLiteAsyncConnection database;
    
            //public NoteDatabase(string dbPath)
            //{
            //    database = new SQLiteAsyncConnection(dbPath);
            //    database.CreateTableAsync<Note>().Wait();
            //}
            public NoteDatabase()
            {
                database = Xamarin.Forms.DependencyService.Get<ISQLite>().GetConnection();
                database.CreateTableAsync<Note>().Wait();
            }

            四、修改 Notes项目下的App.xaml.cs

            public static NoteDatabase Database
            {
                get
                {
                    if (database == null)
                    {
                        //database = new NoteDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Notes.db3"));
                        database = new NoteDatabase();
                    }
                    return database;
                }
            }

            编译执行。

            假期三天,就做了这么件事。无所谓好坏和成败。只是感悟,记录于此,希望能给有相同困扰的开发者一点参考。

    -------------------图片来源于网页截图,如有侵权,告知即删除-------------------

  • 相关阅读:
    Solr的核心操作案例
    分布式锁
    AngularJS——AngularJS实现地址栏取值
    【转】保证消息队列的高可用性
    【转】Spring线程及线程池的使用
    微信支付实现
    分布式id的生成方式——雪花算法
    重载new和delete
    C++工程实践
    语言基础(27):异常处理
  • 原文地址:https://www.cnblogs.com/LiYunQi/p/15786706.html
Copyright © 2011-2022 走看看