i want to use use Sqllite datbase in my Android Game i am developing with Haxe - OpenFl but the app keep crashing when i am trying to query the database. If this is not possible or have any other ways to deal with data in Android kindly let me know as the json and shared objects are not going to work with in my scenario.
i posted this question in the OpenFl community too - but i think it is more related to Haxe then OpenFL.
OpenFl Community Post
What i am doing :
Making a database using DB Browser and saving it to the assets/data/db/data.db
then when app starts i am making a copy of it to the lime.system.System.applicationStorageDirectory
it creates the file in the applicationStorageDirectory
Then i try to connect to the newly created db file and it just connects but right after connecting to it, try to get the name of the db it is connected to
trace("Connected to database " +_conn.dbName ); and it is not showing anything in the trace except the text connected to database.
Ignoring the name i tried to query the Database and it just closes my app without any error or anything i get to know what goes wrong.
My Project.xml
<android target-sdk-version="26" install-location="preferExternal" if="android" />
<android permission="android.permission.WRITE_EXTERNAL_STORAGE"/>
<android permission="android.permission.WRITE_INTERNAL_STORAGE"/>
<haxelib name="openfl" />
<haxelib name="hxcpp" />
<assets path="assets/data/db" rename="db" />
DBClass
package;
import haxe.io.Bytes;
import lime.Assets;
import openfl.system.System;
import sys.FileSystem;
import sys.db.Connection;
import sys.db.Sqlite;
import sys.io.File;
#if android
// Make SQLite work on android by statically compiling the library
import hxcpp.StaticSqlite;
#end
/**
* ...
* #author Sim
*/
class DBManager
{
private var CLONE:String = "db/asset_database.db";
private var NEW:String = "new_db.db";
private var _conn:Connection = null;
public function new()
{
}
public function openDatabase():Void
{
trace("CREATING FILE");
trace("targetPath: " +lime.system.System.applicationStorageDirectory);
//trace("targetPath: " +lime.system.System.applicationDirectory); //Crashing the app
trace("targetPath: " +lime.system.System.documentsDirectory);
trace("targetPath: " +lime.system.System.desktopDirectory);
var targetPath: String = lime.system.System.applicationStorageDirectory+ NEW;
trace("targetPath " + targetPath);
trace("FileSystem.exists(targetPath) " + FileSystem.exists(targetPath));
//Debugging
/*var bytes:Bytes = Assets.getBytes(CLONE);
trace("bytes are here "+bytes);
var content:String = bytes.toString();
trace("content "+content);
File.saveContent(targetPath, content);
trace("Saved");*/
//uncomment when done with errors
/*if (FileSystem.exists(targetPath) == false)
{
var bytes:Bytes = Assets.getBytes(CLONE);
var content:String = bytes.toString();
File.saveContent(targetPath, content);
}*/
var bytes:Bytes = Assets.getBytes(CLONE);
var content:String = bytes.toString();
File.saveContent(targetPath, content);
trace("Saved");
try
{
_conn = Sqlite.open(targetPath+NEW);
}
catch (e:Dynamic)
{
trace("Connection failed with error: "+e);
}
if (_conn != null)
{
trace("Connected to database " +_conn.dbName );
//not getting any database name trying to query
// and KaBoom app gone :D XD
var result = _conn.request("SELECT * FROM TEST");
trace("Query Result "+result.results());
//if i comment then it will go and close the connection too
//without breaking anything O_O
_conn.close();
}
}
}
I took a nap and got the fix in my dreams: :D
The problem is here
_conn = Sqlite.open(targetPath+NEW);
Fix:
_conn = Sqlite.open(targetPath);
Because database name is already in the path :P
var targetPath: String = lime.system.System.applicationStorageDirectory+ NEW;
That’s why always sleep for 8 hours otherwise will end up like me
Related
I have database and i want to import it in my project ,first i added a SQLITe component to my project and insert my DB to ASSETS folder.
All the examples that i have seen it's about how to create database in project, but i already have the database and i want to make some query to display it in my app.
I checked this tutorial:
Use a local database in Xamarin
But it does not work!
This my code:
void conndb ()
{
string dbName = "Chinook_Sqlite.sqlite";
string dbPath = Path.Combine (Android.OS.Environment.ExternalStorageDirectory.ToString (), dbName);
// Check if your DB has already been extracted.
if (!File.Exists(dbPath))
{
using (BinaryReader br = new BinaryReader(Assets.Open(dbName)))
{
using (BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int len = 0;
while ((len = br.Read(buffer, 0, buffer.Length)) > 0)
{
bw.Write (buffer, 0, len);
}
}
}
using (var conn = new SQLite.SQLiteConnection(dbPath))
{
var cmd = new SQLite.SQLiteCommand (conn);
cmd.CommandText = "select * from Album where AlbumId=1;";
var r = cmd.ExecuteQuery<Album> ();
Console.Write (r);
}
}
}
Any help?
You will need to upload your SQLite DB into your Android Device using the File Explorer. You will need to use Android Studio, I do not believe Xamarin Studio has the equivalent tool.
If you start Monitor from the Android Studio (green robot icon on the toolbar, to the left of the help icon) you can then can select File Explorer tab. You should be able to upload your DB File to the location you're calling in your Xamarin C# Code.
I'm coding an app for Android and iOS devices using Flex. I've an SQLite db already created and I want access to his data. I've seen some code looking with google but I'm not able to run nothing.
I would appreciate if you can explain me where I've to put the file.db into my project and if you share me some simple code that allow to connect to file.db and select some rows.
Sorry for my bad english and thanks in advance.
The code bellow will return an object with the data from the database.
package bd
{
public class SQLPadConnection extends SQLStatement
{
protected var _DBFilePatch:String = "DB.sqlite";
protected var _conn:SQLConnection = new SQLConnection();
public function SQLPadConnection()
{
var folder:File = File.userDirectory.resolvePath('com.app.directoryName');
if (!folder.exists) {
folder.createDirectory();
}
_conn.open(folder.resolvePath(_DBFilePatch));
this.sqlConnection = _conn;
super();
}
public function getSQLData(SQL:String = ""):Object
{
if (StringUtil.trim(SQL) != "")
{
this.begin();
this.text = SQL;
this.execute();
this.commit();
return this.getResult();
}
else
return null;
}
}
}
To use that, just do like bellow:
var res:Object = new Object();
var SQL:SQLPadConnection = new SQLPadConnection();
res = SQL.getSQLData("SELECT * FROM TABLE");
res = SQL.getSQLData("INSERT INTO/ UPDATE/ DELETE...");
Hope this help someone.
I'm going to assume you actually meant AIR, this runtime is what allows you to connect to SQLite. In which case there is gobs of information just in the reference alone...
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/data/SQLConnection.html
I'm trying to drop in sqlite support for saving the score and some flags. I need to open the db if it exists, then init game values based on db. If the db does not exist, I need to create/init it. The code below compiles but crashes for unknown reason.
package mygame;
<snip imports>
import sys.db.Types;
class ScoreDB extends sys.db.Object {
public var id : SId;
public var dbscore1 : SInt;
public var dbsound : SInt;
public var dbscore2 : SInt;
}
class mygame extends Sprite {
<snip var defines>
public function new () {
// start sqlite code
sys.db.Manager.initialize();
// db does exist
// then read values
// currentScore = score1.dbscore1;
// doSound = score1.dbsound;
// doScore = score1.dbscore2;
// db does not exist:
var cnx = sys.db.Sqlite.open("mybase.db");
sys.db.Manager.cnx = cnx;
sys.db.TableCreate.create(ScoreDB.manager);
var score1 = new ScoreDB();
score1.id = 0;
score1.dbscore1 = 0;
score1.dbsound = 0;
score1.dbscore2 = 0;
score1.insert();
currentScore = 0;
doSound = 0;
doScore = 0;
cnx.close();
// end sqlite code
super ();
initialize ();
construct ();
newGame ();
}
I actually just solved the same issue.
The problem is that the app is essentially a .zip file and cannot be edited. You need to create (and later access) the DB in the app storage directory.
To access the directory use following code:
private static var localStoragePath:String = openfl.utils.SystemPath.applicationStorageDirectory;
There is a known bug that the IDE's don't show the SystemPath class, but don't mind it, it will compile without problem.
Later, with your common tools you can read and write the directory, create new folders etc.
Here's a quick test, to make sure it works and doesn't crash:
// creating a test folder
sys.FileSystem.createDirectory(openfl.utils.SystemPath.applicationStorageDirectory + "/testFolder");
var form:TextFormat = new TextFormat(null, 22, 0xFFFFFF);
// getting contents of the storage dir
var arr = sys.FileSystem.readDirectory(openfl.utils.SystemPath.applicationStorageDirectory);
var tf:TextField = new TextField();
tf.defaultTextFormat = form;
tf.width = Lib.current.stage.stageWidth;
tf.height = Lib.current.stage.stageHeight;
tf.multiline = true;
tf.wordWrap = true;
tf.text = arr.toString();
addChild(tf);
as you'll see, the newly created folder is there. You can delete the line that creates the folder and you'll see it's safe.
Oh, an don't forget to add Android permissions in the XML file:
<android permission="android.permission.WRITE_EXTERNAL_STORAGE"/>
As far as I know there is no sqlite definitions in openfl. And the ones you use are just normal cpp target definition. I suppose the problem is they don't work on android. Even more: I'm quite sure the api definitions are ok, but it tries to load dll with a wrong name, which probably kills your app without even letting out an error. Try to look into implementation(it is short and easy to understand) and change the dll name.
I'm looking for a code example that demonstrates practical real-world usage of the Storage interface. I'm especially interested in HTML5 implementation. I've just started working on my own proof-of-concept, so I'll post that if no better answers arrive before then.
The Storage interface is introduced in this Google presentation here:
http://playn-2011.appspot.com/
Here's some code that demonstrates the use of the storage interface together with PlayN's JSON interface:
private void loadStoredData() {
// storage parameters
String storageKey = "jsonData";
Json.Object jsonData = PlayN.json().createObject();
// to reset storage, uncomment this line
//PlayN.storage().removeItem(storageKey);
// attempt to load stored data
String jsonString = PlayN.storage().getItem(storageKey);
// if not loaded, create stored data
if ( jsonString == null ) {
DemoApi.log("stored data not found");
jsonData.put("firstWrite", new Date().toString());
// else display data
} else {
jsonData = PlayN.json().parse(jsonString);
DemoApi.log("stored data loaded");
DemoApi.log("data first written at " + jsonData.getString("firstWrite"));
DemoApi.log("data last read at " + jsonData.getString("lastRead"));
DemoApi.log("data last written at " + jsonData.getString("lastWrite"));
}
// update last read
jsonData.put("lastRead", new Date().toString());
// write data (this works in Java -- not in HTML)
// see https://stackoverflow.com/q/10425877/1093087
/*
Json.Writer jsonWriter = PlayN.json().newWriter();
jsonWriter.object(jsonData).done();
jsonString = jsonWriter.write();
*/
// alternative write routine
Json.Writer jsonWriter = PlayN.json().newWriter();
jsonWriter.object();
for ( String key : jsonData.keys() ) {
jsonWriter.value(key, jsonData.getString(key));
}
jsonWriter.end();
jsonString = jsonWriter.write();
// store data as json
PlayN.storage().setItem(storageKey, jsonString);
// confirm
if ( PlayN.storage().isPersisted() ) {
DemoApi.log("data successfully persisted");
} else {
DemoApi.log("failed to persist data");
}
}
There's one little hitch with the Json.Writer that seems a bit buggy that I document in this question here: In the HTML version of PlayN, why does the following JSON-handling code throw an exception?
Is it possible in an air application to start a download, pause it and after that resume it?
I want to download very big files (1-3Gb) and I need to be sure if the connection is interrupted, then the next time the user tries to download the file it's start from the last position.
Any ideas and source code samples would be appreciated.
Yes, you would want to use the URLStream class (URLLoader doesn't support partial downloads) and the HTTP Range header. Note that there are some onerous security restrictions on the Range header, but it should be fine in an AIR application. Here's some untested code that should give you the general idea.
private var _us:URLStream;
private var _buf:ByteArray;
private var _offs:uint;
private var _paused:Boolean;
private var _intervalId:uint;
...
private function init():void {
_buf = new ByteArray();
_offs = 0;
var ur:URLRequest = new URLRequest( ... uri ... );
_us = new URLStream();
_paused = false;
_intervalId = setInterval(500, partialLoad);
}
...
private function partialLoad():void {
var len:uint = _us.bytesAvailable;
_us.readBytes(_buf, _offs, len);
_offs += len;
if (_paused) {
_us.close();
clearInterval(_intervalId);
}
}
...
private function pause():void {
_paused = true;
}
...
private function resume():void {
var ur:URLRequest = new URLRequest(... uri ...);
ur.requestHeaders = [new URLRequestHeader("Range", "bytes=" + _offs + "-")];
_us.load(ur);
_paused = false;
_intervalId = setInterval(500, partialLoad);
}
if you are targeting mobile devices, maybe you should take a look at this native extension: http://myappsnippet.com/download-manager-air-native-extension/ it supports simultaneous resumable downloads with multi-section chunks to download files as fast as possible.