Unity的安装数据库(SQLite)

时间:2018-06-08 04:51:40

标签: c# android sqlite unity3d

我看了太多教程列表,他们都推荐相同的东西。但是,他们没有帮助解决我的问题。

我正在尝试在我的项目中包含一个SQLite数据库,并且在构建PC时,MAC& Linux Standalone(在Windows机器上测试),数据库按预期工作。在Android设备上进行测试时,出现以下错误。

   E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/TBLDatabase.db"
          at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
          at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
          at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0 
          at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 

我认为对connectionString进行修改应该很简单,但这并没有解决我的问题。这就是我到目前为止所做的:

   if (Application.platform != RuntimePlatform.Android)
        {
            // The name of the db.
             tblDatabase = "URI=file:" + Application.dataPath + "/TBLDatabase.db"; //returns the complete path to database file exist.
        }
        else
        {
              tblDatabase = Application.persistentDataPath + "/TBLDatabase.db";

            if (!File.Exists(tblDatabase))
            {
                // if it doesn't ->
                Debug.LogWarning("File \"" + tblDatabase + "\" does not exist. Attempting to create from \"" + Application.dataPath + "!/assets/" + "TBLDatabase.db");
                // open StreamingAssets directory and load the db ->

                // #if UNITY_ANDROID
                var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "TBLDatabase.db");  // this is the path to your StreamingAssets in android
                while (!loadDb.isDone) { }  // CAREFUL here, for safety reasons you shouldn't let this while loop unattended, place a timer and error check
                                            // then save to Application.persistentDataPath
                File.WriteAllBytes(tblDatabase, loadDb.bytes);
            }
        }
        //open db connection
        var connection = new SqliteConnection(tblDatabase);
        connection.Open();
        var command = connection.CreateCommand();

我使用了adb shell并从我的Android设备中提取了数据库,所有内容都符合预期(数据库确实存在,并且它不是空的)。

我相信我拥有所有相关的dll文件,但如果有人能给我一些指导我会很感激。

*************** ****编辑********************************************* *

我根据给出的建议做了以下改动。

我现在正在调用以下方法来启动我的连接并处理数据库请求StartCoroutine(RunDbCode(dbFileName, jsonStudentID, jsonIndiNames, jsonIndiStudentNumbers));

然后我有以下方法:

IEnumerator RunDbCode(string fileName, List jsonStudentID, List jsonIndiNames, List jsonIndiStudentNumbers)
    {
        //Where to copy the db to
        string dbDestination = Path.Combine(Application.persistentDataPath, "data");
        dbDestination = Path.Combine(dbDestination, fileName);

        //Check if the File do not exist then copy it
        if (!File.Exists(dbDestination))
        {
            //Where the db file is at
            string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);

            byte[] result;

            //Read the File from streamingAssets. Use WWW for Android
            if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
            {
                WWW www = new WWW(dbStreamingAsset);
                yield return www;
                result = www.bytes;
            }
            else
            {
                result = File.ReadAllBytes(dbStreamingAsset);
            }
            Debug.Log("Loaded db file");

            //Create Directory if it does not exist
            if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
            }

            //Copy the data to the persistentDataPath where the database API can freely access the file
            File.WriteAllBytes(dbDestination, result);
            Debug.Log("Copied db file");
        }

        //Now you can do the database operation
        //open db connection
        var connection = new SqliteConnection(dbDestination);
        connection.Open();
        var command = connection.CreateCommand();

        // Drop the table if it already exists.
        command.CommandText = "DROP TABLE IF EXISTS existing_individual;";
        command.ExecuteNonQuery();

        var sql = "CREATE TABLE existing_individual (studentID VARCHAR(23), fullName VARCHAR(50), studentNumber VARCHAR(20))";
        command.CommandText = sql;
        command.ExecuteNonQuery();

        //Inserting the exisiting student names returned, into the SQLite DB 

        int count = 0;

        foreach (var individuals in jsonStudentID)
        {
            //looping through the existing students registered for the individual quiz - below has been written to avoid SQL injection
            sql = "INSERT INTO existing_individual (studentID, fullName, studentNumber) VALUES (@jsonStudentID, @jsonIndiNames, @jsonIndiStudentNumbers)";
            command.Parameters.AddWithValue("@jsonStudentID", jsonStudentID[count]);
            command.Parameters.AddWithValue("@jsonIndiNames", jsonIndiNames[count]);
            command.Parameters.AddWithValue("@jsonIndiStudentNumbers", jsonIndiStudentNumbers[count]);

            command.CommandText = sql;
            command.ExecuteNonQuery();

            count++;
        }

        //close the connection
        command.Dispose();
        command = null;
        connection.Close();
        connection = null; 
    }

但是,我仍然遇到以下错误:

06-08 15:26:56.498 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/data/TBLDatabase.db"
      at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
      at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
      at UIHandler+<RunDbCode>c__Iterator3.MoveNext () [0x00000] in <filename unknown>:0 
      at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 
    UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
    UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
    <RequestAllStudentNames>c__Iterator2:MoveNext()
    UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

    (Filename:  Line: -1)
06-08 15:26:56.502 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "URI"
      at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0 
      at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0 
      at UIHandler.CreateIndiButton () [0x00000] in <filename unknown>:0 
      at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0 
      at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 

我还将我的数据库添加到了&#39; StreamingAssets&#39;文件夹如下图所示:

enter image description here

下面还显示了我的插件文件夹的图片,其中包含我的dll文件。

enter image description here

1 个答案:

答案 0 :(得分:7)

关于此主题的大多数教程都已过时。

查看代码并发现一些问题,但我无法判断这些是否是您收到此错误的原因。 {/ 1}}应该在coroutine函数中使用,以便通过在WWW循环中添加loadDb.isDone来完成或等待yield return null完成。您也可以自己提出while请求以及我将在答案中使用的方法。

此外,WWW是旧代码。请使用jar:file://" + Application.dataPath。此外,您不需要Application.streamingAssetsPath。只需使用"URI=file:" + Application.dataPath即可。

我将简单介绍一下如何进行设置。

设置MANAGED代码部分:

1 。转到Unity安装路径

Application.persistentDataPath

复制以下文件:

  • <UnityInstallationDirecory>\Editor\Data\Mono\lib\mono\2.0
  • I18N.MidEast.dll
  • I18N.Other.dll
  • I18N.Rare.dll
  • I18N.West.dll
  • Mono.Data.Sqlite.dll
  • Mono.Data.SqliteClient.dll

到项目的System.Data.dll路径。

这将允许您从<ProjectName>\Assets\Plugins命名空间编译API而不会出现任何错误。

设置UNMANAGED代码部分:

在此步骤中,您需要获取本机Sqlite库。您可以获取source code,构建它并使用它或使用已预编译的binray。

1 。获取 Windows 的本地库

here下载适用于Windows 64位的预编译Mono.Data.Sqlite,并将其放在sqlite3.dll路径中。

如果使用Windows 32位,则从here获取<ProjectName>\Assets\Plugins\x86_64版本并将其放在sqlite3.dll路径中。

2 。获取 Android 的原生图书馆

从中下载 Android ARM处理器的预编译<ProjectName>\Assets\Plugins\x86 here并将其放在libsqlite3.so路径中。

从中下载 Android Intel x86处理器的预编译<ProjectName>\Assets\Plugins\Android\libs\armeabi-v7a here并将其放在libsqlite3.so路径中。

这涵盖Android设备上使用的大多数processors

3 。获取 UWP的本地库

A 。下载WSA文件夹,然后将WSA文件夹放在<ProjectName>\Assets\Plugins\Android\libs\x86路径中。该文件夹包含本机部分。

B 。创建名为&#34; mcs.rsp&#34; &#34; csc.rsp&#34; 在<ProjectName>\Assets\Plugins路径中。

C 。在&#34; mcs.rsp&#34; &#34; csc.rsp&#34; 文件:

<ProjectName>\Assets

D 。在构建UWP时,您必须将托管dll移动到项目的 root 文件夹。因此,将-r:I18N.MidEast.dll -r:I18N.Other.dll -r:I18N.Rare.dll -r:I18N.West.dll -r:Mono.Data.Sqlite.dll -r:Mono.Data.SqliteClient.dll -r:System.Data.dll I18N.MidEast.dllI18N.Other.dllI18N.Rare.dllI18N.West.dllMono.Data.Sqlite.dllMono.Data.SqliteClient.dll移至System.Data.dll路径 <ProjectName>路径。

4 。对于iOS,Linux和Mac,看起来您不必为他们下载任何其他内容或执行此步骤。它们通常内置了本机预编译的Sqlite库。

在Build中包含数据库文件:

1 。在<ProjectName>\Assets\Plugins文件夹中创建一个文件夹,并将其命名为 StreamingAssets 。拼写很重要且它区分大小写。

2 。在此 StreamingAssets 文件夹中输入数据库文件(<ProjectName>\Assets)。

在构建项目后访问数据库文件

Sqlite无法处理构建中 StreamingAssets 文件夹中的文件,因为它是一个只读路径。此外,Android要求您使用TBLDatabase.db API而不是标准WWW API从 StreamingAssets 文件夹中读取。您必须将db文件从System.IO复制到Application.streamingAssetsPath/filename.db

在某些平台上,您需要在Application.persistentDataPath/filename.db中创建一个文件夹,然后将数据保存到该文件夹​​。总是这样做。以下示例代码中的文件夹是&#34; data&#34;这将成为Application.persistentDataPath

3 。由于上述说法,请检查数据库文件是否存在 Application.persistentDataPath/data/filename.db。如果是,请使用Application.persistentDataPath/data/filename.db作为数据库操作的路径。如果没有,请从#4继续。

4 。将数据库文件从 StreamingAssets 文件夹中复制并复制到Application.persistentDataPath/data/filename.db

在某些平台上,您需要在Application.persistentDataPath中创建一个文件夹,然后将数据保存到该文件夹​​。总是这样做。以下示例中的文件夹是&#34;数据&#34;。

检测这是否为Android并使用Application.persistentDataPathWWW读取文件。使用Application.streamingAssetsPath/filename.db在Android以外的任何其他内容上阅读。在您的示例中,您使用了File.ReadAllBytes。在我的示例中,我只是检查路径是否包含Application.platform"://"来执行此操作。

5 。一旦您阅读该文件,请将您刚刚阅读的数据写入:/// Application.persistentDataPath/data/filename.db。现在,您可以使用此路径进行数据库操作。

6 .Prefix File.WriteAllBytes"URI=file:"路径,并且应该在使用Sqlite API的数据库操作中使用该路径。

了解所有这些是非常重要的,以便在某些事情发生变化时修复它但我已经完成了步骤#3 #6 下面。

Application.persistentDataPath/data/filename.db

<强>用法

IEnumerator RunDbCode(string fileName)
{
    //Where to copy the db to
    string dbDestination = Path.Combine(Application.persistentDataPath, "data");
    dbDestination = Path.Combine(dbDestination, fileName);

    //Check if the File do not exist then copy it
    if (!File.Exists(dbDestination))
    {
        //Where the db file is at
        string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);

        byte[] result;

        //Read the File from streamingAssets. Use WWW for Android
        if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
        {
            WWW www = new WWW(dbStreamingAsset);
            yield return www;
            result = www.bytes;
        }
        else
        {
            result = File.ReadAllBytes(dbStreamingAsset);
        }
        Debug.Log("Loaded db file");

        //Create Directory if it does not exist
        if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
        }

        //Copy the data to the persistentDataPath where the database API can freely access the file
        File.WriteAllBytes(dbDestination, result);
        Debug.Log("Copied db file");
    }

    try
    {
        //Tell the db final location for debugging
        Debug.Log("DB Path: " + dbDestination.Replace("/", "\\"));
        //Add "URI=file:" to the front of the url beore using it with the Sqlite API
        dbDestination = "URI=file:" + dbDestination;

        //Now you can do the database operation below
        //open db connection
        var connection = new SqliteConnection(dbDestination);
        connection.Open();

        var command = connection.CreateCommand();
        Debug.Log("Success!");
    }
    catch (Exception e)
    {
        Debug.Log("Failed: " + e.Message);
    }
}