Now i am developing a phonegap application:
In which the insertion operation in sqlite db getting locked when app go to background in iOS ( The same worked in android ). When the application is in forground the databse operations work smoothly.
Why this happening and how can i handle this ?
I think you've used for loop for insertion, Javascript in asynchronous in nature.
You need to execute batch sql queries in the following style not with for loop
db = window.openDatabase("Demo", "1.0", "BusinessApp", 200000);
insertIndex = 0;
var insertCounter = 0;
insertBatch = function(array, arrayLength, tableName, fieldQuestions, cb) {
console.log("Inserting Record :" + insertCounter);
if (insertIndex < arrayLength) {
db.transaction(function(tx) {
var sql = 'INSERT OR IGNORE INTO ' + tableName + ' VALUES ' + fieldQuestions;
console.log("sql: " + sql);
console.log("sql:------------- ");
console.log(array[insertIndex]);
tx.executeSql(sql, array[insertIndex],
function(tx, res) {
insertIndex++;
console.log("Insert success");
insertBatch(array, arrayLength, tableName, fieldQuestions, cb);
}, function() {
console.log("Insert failure");
});
});
} else {
insertIndex = 0;
cb();
}
}
db.transaction(populateDB, errorCB, successCB);
function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS ?', ["news"], function() {
console.log("success drop")
}, function() {
console.log("failure drop")
});
tx.executeSql('CREATE TABLE IF NOT EXISTS news (news_id INTEGER,news_header TEXT,news_content TEXT)');
}
function errorCB() {
alert("Error: Init.js ErrorCB");
}
function successCB() {
//alert();
console.log('DB Created Success');
var dataArray = []
dataArray.push([1, "ONE", "First News Content"])
dataArray.push([2, "TWO", "2 News Content"])
dataArray.push([3, "THREE", "2 News Content"])
dataArray.push([4, "FOUR", "4 News Content"])
insertBatch(dataArray, dataArray.length, "news", "(?,?,?)", success)
function success() {
console.log("all success")
}
}
So Every executeSQL function requires previous executeSQL success callback
Related
I use SQlite in a Cordova app built with AngularJS. I bootstrap the app onDeviceReady() and then check if a database and a specific table exist and based on the result do certain things. The app works as expected in Android but in iOS it runs the fist time and then it gets blocked on the white page of the simulator or device! Can anyone give me an insight on how to resolve this issue?
var db = window.sqlitePlugin.openDatabase(
// options
{
name: "users_info.db",
location: 2
},
// success callback
function (msg) {
// console.log("success: " + msg);
// console.log(msg);
},
// error callback
function (msg) {
// console.log("error: " + msg);
}
);
db.transaction(function (tx) {
tx.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name='user'", [], function (tx, result) {
if (result.rows.length == 0) {
console.log('theres no table with this name ');
$scope.$apply(function () {
$location.path('/login');
});
} else {
tx.executeSql(
"select * from user;",
[],
function (tx, res) {
var row = res.rows.item(0);
console.log(row.role);
if (row.role == 4) {
$scope.$apply(function () {
userService.roleid = '4';
userService.userid = row.userid;
$location.path('/student');
});
}
});
console.log('this table exists');
}
});
});
I have also tried the following codes to open the database but again in iOS the app freezes after the first run
var db = $cordovaSQLite.openDB({ name: "users_info.db", location: 1, iosDatabaseLocation: 'default' },
// success callback
function (msg) {
// console.log("success: " + msg);
console.log(msg);
console.log('success');
},
// error callback
function (msg) {
console.log("error: " + msg);
console.log('error');
}
);
I found out that the reason of the issue was that my page containing the code snippet for the SQLite database refreshed twice when loading which caused the codes to read from and write to the SQlite database twice simultaneously which cause the database and consequently the iOS app to freeze. I made sure the codes run only once and it worked like a charm!
I want to add a key on the fly to an object, for example, I run a query to get the Items (class Test), next for each item a count the number of Favorite Records it has (suppose that 1 is the UserId). But, when a I call the function from iOS (Swift), I'm getting the list of PFObject, but not has the dynamic key (I don't want to save the Key on Items (class Test), because that's only to get the data on the fly).
Parse.Cloud.define("getAllItemsTestInstag",function(request,response){
var query = new Parse.Query("Test");
query.find().then(function(tests){
var elementProcessed = 0;
for (var i = 0; i < tests.length; i++) {
var testItem = tests[i];
var innerQuery = new Parse.Query("Favorite");
innerQuery.equalTo("UserId",1);
innerQuery.equalTo("Test",testItem);
innerQuery.count({
success: function(count){
if(count == 0){
testItem.set("related",true);
}else{
testItem.set("related",false)
}
console.log("Relacion conteo : " + count);
elementProcessed++;
if(elementProcessed == tests.length){
response.success(tests);
}
},
error: function(error){
console.log(error);
}
});
};
});
});
You can just add the new key, without saving and then return it in response.success. Something like this:
...
innerQuery.count({
success: function(count){
if(count == 0){
testItem.set("related",true);
}else{
testItem.set("related",false)
}
testItem.count = count;
console.log("Relacion conteo : " + count);
elementProcessed++;
if(elementProcessed == tests.length){
response.success(tests);
}
},
error: function(error){
console.log(error);
}
});
...
I am using a background job to query a json with thousands of objects to initially populate my database. I have also implemented the beforesave function to prevent any duplicate entries. However, once I implemented this, it seems my background job called response.error and does not save all objects. It looks like I might be exceeding the requests/sec? I would really appreciate if someone could take a look at my code and tell me why it is not saving all entries successfully.
Here is my background job:
Parse.Cloud.job("testing", function(request, response) {
var json;
Parse.Cloud.httpRequest({
url: stringURL + pageNumber.toString(),
success: function(httpResponse) {
json = httpResponse.data;
console.log("total is: " + json["meta"].total);
console.log("object 1 is: " + json["events"][1].title);
return json;
}
//after getting the json, save all 1000
}).then(function() {
//helper function called
saveObjects(json).then(function() {
response.success("success");
},
function(error) {
response.error("nooooo");
});
});
});
function saveObjects(json) {
var promises = [];
for(var i = 0; i < 1000; i++) {
var newEvent = new Event();
promises.push(newEvent.save(new Event(json["events"][i])));
}
return Parse.Promise.when(promises);
}
Here is my beforesave code:
Parse.Cloud.beforeSave("Event", function(request, response) {
var newEvent = request.object;
var Event = Parse.Object.extend("Event");
var query = new Parse.Query("Event");
query.equalTo("title", newEvent.get("title"));
query.equalTo("datetime_utc", newEvent.get("datetime_utc"));
query.equalTo("url", newEvent.get("url"));
query.first({
success: function(temp) {
response.error({errorCode:123,errorMsg:"Event already exist!"});
},
error: function(error) {
response.success();
}
});
});
Thanks I really appreciate any help... I've been stuck for a while.
If it's a request rate issue, then you could probably use something like node-function-rate-limit but it's fairly simple to write your own rate limiting batcher. See doInBatches() below.
Also, when using promise-returning methods that also offer a "success:..." callback, it's better not to mix the two styles. It may behave as expected but you are denied the opportunity to pass results from the "success:..." callback to the rest of the promise chain. As you can see below, the "success:..." code has simply been shuffled into the .then() callback.
Parse.Cloud.job("testing", function(request, response) {
Parse.Cloud.httpRequest({
url: stringURL + pageNumber.toString()
}).then(function(httpResponse) {
var json = httpResponse.data;
// console.log("total is: " + json.meta.total);
// console.log("object 1 is: " + json.events[1].title);
/* helper function called */
doInBatches(json.events, 30, 1000, function(evt, i) {
var newEvent = new Event();
return newEvent.save(new Event(evt));
}).then(function() {
response.success('success');
}, function(error) {
response.error('nooooo');
});
});
});
// Async batcher.
function doInBatches(arr, batchSize, delay, fn) {
function delayAsync() {
var p = new Parse.Promise();
setTimeout(p.resolve, delay);
return p;
}
function saveBatch(start) {
if(start < arr.length) {
return Parse.Promise.when(arr.slice(start, start+batchSize).map(fn))
.then(delayAsync) // delay between batches
.then(function() {
return saveBatch(start + batchSize);
});
} else {
return Parse.Promise.as();
}
}
return saveBatch(0);
}
I can't see how or why the beforesave code might affect things.
The Phonegap/Cordova project using https://github.com/brodysoft/Cordova-SQLitePlugin for using SQLite. There is minor changes made in MainViewController.m to use pre-populated databse (however it might not effect the problem).
The database detecting very well here is the databaase detecting code
function onDeviceReady() {
db = window.sqlitePlugin.openDatabase({name: "database.db"});
db.transaction(queryDB, errorCB);
function queryDB(tx) {
console.log("started");
tx.executeSql("select * from user_info", [], function(tx, res) {
console.log("res.rows.length: " + res.rows.length);
});
}
function errorCB(err) {
console.log("Error processing SQL : "+err.message);
}
}
Result
res.rows.length: 1
Here is the retrieving data code which takes about 10 sec to show result :(
function callMe (argument) {
db.transaction(buttonqueryDB, buttonerrorCB);
function buttonqueryDB(transaction) {
console.log("going to query");
transaction.executeSql('SELECT * FROM home_word', [], function(transaction, result) {
console.log("total itemes " + result.rows.length);
if (result != null && result.rows != null) {
for (var i = 0; i < 10; i++) {
var row = result.rows.item(i);
console.log("this result : " +row.word);
}
}
});
}
function buttonerrorCB(err) {
console.log("Error processing SQL : "+err.message);
}
}
The going to query shows quickly and query result display takes about 10sec
I am trying to send a voice recording that I recorded via the Media plugin.
When I try to send the file I get this FileError.NOT_FOUND_ERR error:
Error opening file /myRecording100.wav: Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0xa358640 {NSFilePath=/myRecording100.wav, NSUnderlyingError=0xa34fb30 "The operation couldn’t be completed. No such file or directory"}
2014-08-06 17:02:26.919 Bring Me[40961:c07] FileTransferError {
code = 1;
source = "/myRecording100.wav";
target = "http://XXXX.xom";
}
However, I can play the voice recording after recording it.
Why would I be able to play the file (showing that the file was recorded and saved correctly) but FileTransfer be unable to send it?
Here is my code (for ios):
var my_recorder = null;
var mediaFileFullName = null; // iOS
var mediaRecFile = "myRecording100.wav";
var checkFileOnly = false;
/******
Call when start recording
******/
function startRecording() {
checkFileOnly = false;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccessFileSystem, function() {
console.log("***test: failed in creating media file in requestFileSystem");
});
}
function onSuccessFileSystem(fileSystem) {
if (checkFileOnly === true) {
// Get File and send
fileSystem.root.getFile(mediaRecFile, { create: false, exclusive: false }, onOK_GetFile, onFail_GetFile);
}
else {
// Create File
fileSystem.root.getFile(mediaRecFile, { create: true, exclusive: false }, onOK_SaveFile, onFail_GetFile);
}
}
/* Save the file*/
function onOK_SaveFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
my_recorder = new Media(mediaFileFullName,
function() { document.location ="address_form.html"; // Redirect the user to an other page },
function(err) { console.log("playAudio():callback Record Error: "+err);}
);
my_recorder.startRecord();
}
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
/*
// Read the recorded file is WORKING !
my_player = new Media(mediaFileFullName, onMediaCallSuccess, onMediaCallError);
my_player.play();
*/
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(mediaFileFullName, "https://SERVER_ADDRESS", win, fail, options);
}
/******
Called when stop recording
******/
function stopRecording() {
if (my_recorder) {
my_recorder.stopRecord();
}
}
Since the v1.0 of File plugin, to upload a file in the filesystem via the file-transfer plugin, you'll need to use the .toURL() method to access to it.
If you are upgrading to a new (1.0.0 or newer) version of File, and
you have previously been using entry.fullPath as arguments to
download() or upload(), then you will need to change your code to use
filesystem URLs instead.
FileEntry.toURL() and DirectoryEntry.toURL() return a filesystem URL
of the form
So the correct code is :
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(fileEntry.toURL(), "https://SERVER_ADDRESS", win, fail, options);
}
I got the exact same issue on iOS,and FileUploadOptions didn't work for me.
In case someone is struggling as well, the solution for me has been to switch to LocalFileSystem.Temporary.
Here there is a snippet which shows a full example (not tested on Android):
var accessType = LocalFileSystem.TEMPORARY; // It was LocalFileSystem.PERSISTENT;
/** Utility function to return a fileEntry together with the metadata. */
var getFile = function(name, create, successCallback, failCallback) {
WL.Logger.debug("Request for file " + name + " received, create is " + create + ".");
var onSuccessFileSystem = function(fileSystem) {
fileSystem.root.getFile(name, { create: create, exclusive: false },
function(fileEntry){
WL.Logger.debug("Success, file entry for " + name + " is " + JSON.stringify(fileEntry));
fileEntry.getMetadata(function(metadata){
WL.Logger.debug("File entry " + name + " metadata is: " + JSON.stringify(metadata));
successCallback(fileEntry, metadata);
}, function(err) {
WL.Logger.debug("Fail to retrieve metadata, error: " + JSON.stringify(err));
if(failCallback) failCallback(err);
});
},
function(err) {
WL.Logger.error("Failed to retrieve the media file " + name + ".");
if(failCallback) failCallback(err);
});
}
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(accessType, 0, onSuccessFileSystem, function(err) {
WL.Logger.error("Failed to access file system.");
if(failCallback) failCallback(err);
});
};
var Recorder = declare([ ], {
mediaSrc : null,
mediaObj : null,
constructor : function(data, domNode){
this.mediaSrc = "new_recording.wav";
},
startRecord : function() {
var self = this;
var startRecording = function(source) {
var onMediaCallSuccess = function() { WL.Logger.debug("Media object success."); };
var onMediaCallError = function(err) { WL.Logger.error("Error on the media object: " + JSON.stringify(err)); };
self.mediaObj = new Media(source, onMediaCallSuccess, onMediaCallError);
self.mediaObj.startRecord();
};
// On iOS, first I need to create the file and then I can record.
if (deviceCheck.phone.ios) {
WL.Logger.debug("iOS detected, making sure the file exists.");
getFile(this.mediaSrc, true, function(fileEntry){ startRecording(fileEntry.fullPath); });
} else {
if (!deviceCheck.phone.android)
WL.Logger.warn("Don't know the device, trying to record ...");
else
WL.Logger.debug("Android detected.");
startRecording(this.mediaSrc);
}
},
stopRecord : function() {
this.mediaObj.stopRecord();
this.mediaObj.release();
},
play: function() {
var p,
playSuccess = function() { WL.Logger.debug("Play success."); p.release(); },
playFail = function() { WL.Logger.debug("Play fail."); };
p = new Media(this.mediaSrc, playSuccess, playFail);
p.play();
},
getData : function(successCallback, failCallback) {
var fileName = (deviceCheck.phone.android ? "/sdcard/" : "") + this.mediaSrc;
WL.Logger.debug("Asking for the file entry ... ");
getFile(this.mediaSrc, false,
function(fileEntry, metadata) {
WL.Logger.debug("Success: I found a file entry: " + fileEntry.nativeURL + ", size is " + metadata.size);
fileEntry.file(function(file) {
WL.Logger.debug("Success: file retrieved!");
var reader = new FileReader();
reader.onloadend = function(evt) {
WL.Logger.debug("Sending content and event data to success callback.");
successCallback(this.result, metadata, evt);
};
reader.readAsDataURL(file);
}, function(err){
WL.Logger.error("Error: Impossible to retrieve the file");
failCallback(err);
})
}, function(err){
WL.Logger.error("Fail: no file entry found: " + JSON.stringify(err));
failCallback(err);
});
}
});
There is a bit of Worklight (debug output) and dojo (declare), but this code could be useful as reference.