c# - Base de datos de configuración(SQLite) para Unity
android unity3d (1)
La mayoría de los tutoriales sobre este tema están desactualizados.
Miré el código y encontré algunos problemas, pero no puedo decir si esa es la razón por la que está recibiendo este error.
WWW
debe utilizar en una función de rutina para que pueda ceder o esperar a que
loadDb.isDone
se complete agregando un
yield return null
dentro del bucle while.
También puede entregar la solicitud
WWW
sí misma y ese es el método que usaré en mi respuesta.
Además,
jar:file://" + Application.dataPath
es un código antiguo. Use
Application.streamingAssetsPath
para eso. Además, no necesita
"URI=file:" + Application.dataPath
. Simplemente use
Application.persistentDataPath
para eso .
Acabo de poner una instrucción sobre cómo hacer la configuración.
Configuración de la parte del código GESTIONADO:
1. Vaya a la ruta de instalación de Unity
<UnityInstallationDirecory>/Editor/Data/Mono/lib/mono/2.0
Copia los siguientes archivos:
-
I18N.MidEast.dll
-
I18N.Other.dll
-
I18N.Rare.dll
-
I18N.West.dll
-
Mono.Data.Sqlite.dll
-
Mono.Data.SqliteClient.dll
-
System.Data.dll
a la ruta
<ProjectName>/Assets/Plugins
.
Esto le permitirá compilar la API desde el espacio de nombres
Mono.Data.Sqlite
sin ningún error.
Configuración de la parte del código UNMANAGED:
En este paso, deberá obtener la biblioteca nativa de Sqlite. Puede obtener el código fuente , compilarlo y usarlo o usar un binray ya compilado previamente.
1. Obtener la biblioteca nativa para Windows
Descargue el
sqlite3.dll
precompilado para Windows 64 bit desde
here
, y póngalo en la ruta
<ProjectName>/Assets/Plugins/x86_64
.
Si usa Windows de 32 bits, obtenga la versión
sqlite3.dll
desde
here
y póngala en la ruta
<ProjectName>/Assets/Plugins/x86
.
2. Obtenga la biblioteca nativa para Android
Descargue el
procesador
libsqlite3.so
precompilado para
Android ARM
desde
here
y
<ProjectName>/Assets/Plugins/Android/libs/armeabi-v7a
ruta
<ProjectName>/Assets/Plugins/Android/libs/armeabi-v7a
.
Descargue el
libsqlite3.so
precompilado para el
procesador Intel x86
de
Android
desde
here
y
libsqlite3.so
en la
libsqlite3.so
<ProjectName>/Assets/Plugins/Android/libs/x86
.
Esto cubre la mayoría de los processors utilizados en dispositivos Android.
3. Obtener la biblioteca nativa para UWP
A
.Descargue la carpeta WSA y luego coloque la carpeta WSA en la ruta
<ProjectName>/Assets/Plugins
.
Esa carpeta contiene la parte nativa.
B
.Create 2 archivos llamados
"mcs.rsp"
y
"csc.rsp"
en la ruta
<ProjectName>/Assets
.
C. Agregue lo siguiente dentro de los archivos "mcs.rsp" y "csc.rsp" :
-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
D.
Deberá mover las dll administradas a la carpeta
raíz
del proyecto cuando cree para UWP.
Entonces, mueva
I18N.MidEast.dll
,
I18N.Other.dll
,
I18N.Rare.dll
,
I18N.West.dll
,
Mono.Data.Sqlite.dll
,
Mono.Data.SqliteClient.dll
,
System.Data.dll
al
<ProjectName>
ruta
<ProjectName>
no
<ProjectName>/Assets/Plugins
ruta.
4. Para iOS, Linux y Mac, parece que no tiene que descargar nada más para ellos ni hacer este paso. Por lo general, tienen las bibliotecas nativas de Sqlite precompiladas incorporadas.
Incluyendo el archivo de base de datos en la compilación:
1
.Cree una carpeta en su carpeta
<ProjectName>/Assets
y asígnele el nombre
StreamingAssets
.
La ortografía cuenta y distingue entre mayúsculas y minúsculas.
2
.Ponga el archivo de base de datos (
TBLDatabase.db
) en esta carpeta
StreamingAssets
.
Accediendo al archivo de base de datos después de construir el proyecto
Sqlite no puede trabajar en archivos en la carpeta
StreamingAssets
en una compilación ya que es una ruta de solo lectura.
Además, Android requiere que uses la
WWW
API en lugar de la API estándar de
System.IO
para leer desde la carpeta
StreamingAssets
.
Debe copiar el archivo db de
Application.streamingAssetsPath/filename.db
a
Application.persistentDataPath/filename.db
.
En algunas plataformas, se requiere que cree una carpeta dentro de
Application.persistentDataPath
y guarde los datos en esa carpeta.
Siempre hacer eso
La carpeta en el código de ejemplo a continuación es "datos", por lo que se convertirá en
Application.persistentDataPath/data/filename.db
.
3.
Debido a la declaración anterior, verifique si el archivo de base de datos existe en
Application.persistentDataPath/data/filename.db
.
Si lo hace, use
Application.persistentDataPath/data/filename.db
como una ruta para la operación de su base de datos.
Si no es así, continúe desde el # 4.
4
.Lea y copie el archivo de base de datos de la carpeta
StreamingAssets
a
Application.persistentDataPath
En algunas plataformas, se requiere que cree una carpeta dentro de
Application.persistentDataPath
y guarde los datos en esa carpeta.
Siempre hacer eso
La carpeta en el ejemplo de abajo es "datos".
Detecte si se trata de Android y use la lectura
WWW
en el archivo de
Application.streamingAssetsPath/filename.db
.
Use
File.ReadAllBytes
para leerlo en otra cosa que no sea Android.
En tu ejemplo, usaste
Application.platform
para eso.
En mi ejemplo, simplemente verificaré si la ruta contiene
"://"
o
:///
para hacer eso.
5.
Una vez que lea el archivo, escriba los datos que acaba de leer en
Application.persistentDataPath/data/filename.db
con
File.WriteAllBytes
.
Ahora, puede utilizar esta ruta para la operación de su base de datos.
6
.Prefix
"URI=file:"
a la ruta
Application.persistentDataPath/data/filename.db
y esa es la ruta que debe usarse en la operación de su base de datos con la API de Sqlite.
Es muy importante que entiendas todo esto para solucionarlo cuando algo cambie, pero ya he hecho los pasos 3 a 6 a continuación.
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);
}
}
Uso
string dbFileName = "TBLDatabase.db";
void Start()
{
StartCoroutine(RunDbCode(dbFileName));
}
He visto demasiados tutoriales para enumerar y todos recomiendan lo mismo. Sin embargo, no han ayudado a resolver mi problema.
Estoy tratando de incluir en mi proyecto una base de datos SQLite, y al compilar para PC, MAC y Linux Standalone (pruebas en una máquina con Windows), la base de datos funciona como se esperaba. Al realizar pruebas en un dispositivo Android, obtengo los siguientes errores.
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
Pensé que hacer una enmienda a la cadena de conexión debería ser lo suficientemente simple, pero eso no ha resuelto mi problema. Esto es lo que tengo hasta ahora:
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();
He usado adb shell y extraje la base de datos de mi dispositivo Android y todo es como se esperaba (la base de datos existe y no está vacía).
Creo que tengo todos los archivos dll relevantes, pero si alguien me pudiera dar alguna orientación, lo apreciaría.
*************************************************** *EDITAR**********************************************
Desde entonces he hecho las siguientes modificaciones basadas en el consejo dado.
Ahora estoy llamando al siguiente método para iniciar mi conexión y manejar solicitudes de
StartCoroutine(RunDbCode(dbFileName, jsonStudentID, jsonIndiNames, jsonIndiStudentNumbers));
datos
StartCoroutine(RunDbCode(dbFileName, jsonStudentID, jsonIndiNames, jsonIndiStudentNumbers));
Entonces tengo el siguiente método:
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;
}
Sin embargo, todavía estoy recibiendo los siguientes errores:
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
También agregué mi base de datos a la carpeta ''StreamingAssets'' como se muestra en la siguiente imagen:
A continuación también se muestra una imagen de la carpeta de mis complementos que contiene mis archivos dll.