Parallel file upload XMLHttpRequest requests and why they won't work - javascript

I am trying to upload many (3 for now) files in parallel using XMLHttpRequest. If have some code that pulls them from a list of many dropped files and makes sure that at each moment I am sending 3 files (if available).
Here is my code, which is standard as far as I know:
var xhr = item._xhr = new XMLHttpRequest();
var form = new FormData();
var that = this;
angular.forEach(item.formData, function(obj) {
angular.forEach(obj, function(value, key) {
form.append(key, value);
});
});
form.append(item.alias, item._file, item.file.name);
xhr.upload.onprogress = function(event) {
// ...
};
xhr.onload = function() {
// ...
};
xhr.onerror = function() {
// ...
};
xhr.onabort = function() {
// ...
};
xhr.open(item.method, item.url, true);
xhr.withCredentials = item.withCredentials;
angular.forEach(item.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
xhr.send(form);
Looking at the network monitor in Opera's developer tools, I see that this kinda works and I get 3 files "in progress" at all times:
However, if I look the way the requests are progressing, I see that 2 of the 3 uploads (here, the seemingly long-running ones) are being put in status "Pending" and only 1 of the 3 requests is truly active at a time. This gets reflected in the upload times as well, since no time improvement appears to happen due to this parallelism.
I have placed console logs all over my code and it seems like this is not a problem with my code.
Are there any browser limitations to uploading files in parallel that I should know about? As far as I know, the AJAX limitations are quite higher in number of requests than what I use here... Is adding a file to the request changing things?

This turned out to be ASP.NET causing the issue.
Multiple requests coming from the same SessionId get serialized, because they lock the session object.
See here.
My fix was to make the session read-only for this particular action. That way, no locking was required.
This is my code (original code taken from here):
public class CustomControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return SessionStateBehavior.Default;
}
var actionName = requestContext.RouteData.Values["action"].ToString();
MethodInfo actionMethodInfo;
var methods = controllerType.GetMethods(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
actionMethodInfo = methods.FirstOrDefault(x => x.Name == actionName && x.GetCustomAttribute<ActionSessionStateAttribute>() != null);
if (actionMethodInfo != null)
{
var actionSessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (actionSessionStateAttr != null)
{
return actionSessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior Behavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior behavior)
{
this.Behavior = behavior;
}
}
// In your Global.asax.cs
protected void Application_Start(object sender, EventArgs e)
{
// .........
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
// You use it on the controller action like that:
[HttpPost]
[Authorize(Roles = "Administrators")]
[ValidateAntiForgeryToken]
[ActionSessionState(SessionStateBehavior.ReadOnly)]
public async Task<ActionResult> AngularUpload(HttpPostedFileBase file){}
And here is the glorious result:

The HTTP/1.1 RFC
Section 8.1.4 of the HTTP/1.1 RFC says a “single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.
Read more here: Roundup on Parallel Connections

Related

Miagration Asp.Net Core 2 to .Net 6 gave me XMLHttpRequest problems

I folks, I just migrated my ASP.Net Core 2 MVC app to .Net 6, but since that, I have a weird problem: my XMLHttpRequest responses texts are always empty, "{}" or [{},{},{},{}] for arrays, despite my backend really returning data.
Here's an example of a controler method (TestLoad) returning a simple class (TestClass). Notice that when I break on the return line, the value returned is ok, or at least I don't see anything wrong (see image for debug infos):
backend
public class TestClass
{
public int id = 0;
public string title = "Title";
public bool active = false;
}
public JsonResult TestLoad()
{
TestClass testClass = new TestClass();
testClass.id = 10;
testClass.title = "Yeah man!";
testClass.active = true;
JsonResult jsonRes = Json(testClass);
return jsonRes;
}
But once on the front end, I got an empty object, not undefined nor null, but really an empty object (see image for debug infos):
frontend
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
var dt = JSON.parse(xmlhttp.responseText);
if (dt == 'err') {
alert('error');
}
else if (dt !== null) {
alert(dt.title);
}
}
else {
alert(xmlhttp.status);
}
}
}
ldwait(false, false);
xmlhttp.open("GET", rv + "ajxGame/TestLoad", true);
xmlhttp.setRequestHeader('Cache-Control', 'no-store');
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.send();
Any help would be greatly appreciated since I completely clueless of what happened. Again, my code hasn't changed, but my project has migrated from .Net Core 2 to .Net 6.
Thank you
i would add a response type, and replace onreadystatechange with onload
xmlhttp.responseType = 'json';
xmlhttp.onload = () => {
console.log("load - "+ JSON.stringify(xhr.response));
var data = xhr.response;
}
Are you using Razor pages in your Asp.net core 6 ?
If so, the way you call your method maybe the issue. For post requests, call from the client ?handler=TestLoad and on the server make sure the method name is OnPostTestLoad().
Get requests are disabled by default and need to be enabled.
Microsoft Warning Message
For more info, check this link from Microsoft docs
Another issue maybe the way you return your class. Try returning it as a JSON object instead.
return new JsonResult(new {objClass: testClass});
And on the client side, get your class as an object property
let objClass = result.objClass;
Actually, my error was simply that I forgot to add { get; set; } on each property of my TestClass in the backend part! It worked without it in .Net Core 2, but it seems like this is now mandatory in .Net 6
public class TestClass
{
public int id {get; set;} = 0;
public string title {get; set;} = "Title";
public bool active {get; set;} = false;
}
...thanks to those who attempted an answer!

Server Sent Events with AJAX: How to resolve SSE GET with XHR POST?

I'm trying to resolve an issue between, what I perceive is, AJAX and Server Sent Events. I have an application that does a post with some instructions to the controller, and I would like the controller to send some commentary back as an event to let the user know that the action requested has been performed (can have errors or take a while).
The idea is that the user can send a package of different instructions through the client, and the server will report through SSE when each of these actions are completed.
The problem I see through Fiddler is that when the post is performed, the response that it gets back contains my eventsource message that I would like used. However, the eventsource code also appears to call a GET, in which it appears to want that eventsource message. Because it doesn't get that, the connection repeatedly closes.
I currently have some controller code like so:
[System.Web.Http.HttpPost]
public void Stop(ProjectViewModel model)
{
ProjectManager manager = new ProjectManager();
if (model.Servers != null && model.Servers.Count != 0)
{
string machine = model.Servers[0];
foreach (string service in model.Services)
{
manager.StopService(service, machine);
Message("stop", service);
}
}
}
and in my view, both Ajax/XHR and server sent events set up like so:
var form = document.getElementById("submitform");
form.onsubmit = function (e) {
// stop the regular form submission
e.preventDefault();
// collect the form data while iterating over the inputs
var data = {};
for (var i = 0, ii = 2; i < ii; ++i) {
var input = form[i];
if (input.name == "Servers") {
data[input.name] = document.getElementById("ServerSelect").options[document.getElementById("ServerSelect").selectedIndex].text;
}
else if (input.name == "Services")
data[input.name] = document.getElementById("ServiceSelect").options[document.getElementById("ServiceSelect").selectedIndex].text;
}
if (action) { data["action"] = action };
// construct an HTTP request
var xhr = new XMLHttpRequest();
if (action == "stop") {
xhr.open(form.method, '/tools/project/stop', true);
}
if (action == "start") {
xhr.open(form.method, '/tools/project/start', true)
}
xhr.setRequestHeader('Content-Type', 'application/json; charset=urf-8');
// send the collected data as JSON
xhr.send(JSON.stringify(data));
xhr.onloadend = function () {
// done
};
};
function events() {
if (window.EventSource == undefined) {
// If not supported
document.getElementById('eventlog').innerHTML = "Your browser doesn't support Server Sent Events.";
} else {
var source = new EventSource('../tools/project/Stop');
source.addEventListener("message", function (message) { console.log(message.data) });
source.onopen = function (event) {
document.getElementById('eventlog').innerHTML += 'Connection Opened.<br>';
console.log("Open");
};
source.onerror = function (event) {
if (event.eventPhase == EventSource.CLOSED) {
document.getElementById('eventlog').innerHTML += 'Connection Closed.<br>';
console.log("Close");
}
};
source.onmessage = function (event) {
//document.getElementById('eventlog').innerHTML += event.data + '<br>';
var newElement = document.createElement("li");
newElement.textContent = "message: " + event.data;
document.getElementById("eventlog").appendChild(newElement)
console.log("Message");
};
}
};
I'm somewhat new to web development, and I'm not sure how to resolve this issue. Is there a way I can have the eventsource message read from that POST? Or have it sent to the GET instead of being sent as a response to the POST? Overall, it seems that the most damning issue is that I can't seem to get the event messages sent to the GET that is requested by the eventsource api.
EDIT: Since posting this, I tried creating a new method in the controller that specifically handles eventsource requests, but it appears that the event response still somehow ends up in the POST response body.
public void Message(string action, string service)
{
Response.ContentType = "text/event-stream";
Response.CacheControl = "no-cache";
//Response.Write($"event: message\n");
if (action == "stop")
{
Response.Write($"data: <li> {service} has stopped </li>\n\n");
}
Response.Flush();
Thread.Sleep(1000);
Response.Close();
}
I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:
https://code.msdn.microsoft.com/How-to-create-and-access-447ada98
My final implementation looks like this:
public void Stop(ProjectViewModel model)
{
ProjectManager manager = new ProjectManager();
if (model.Servers != null && model.Servers.Count != 0)
{
string machine = model.Servers[0];
foreach (string service in model.Services)
{
manager.StopService(service, machine);
model.events.Add(service, "stopped");
this.Session["Events"] = model.events;
}
}
//return View(model);
}
public void Message(ProjectViewModel model)
{
Thread.Sleep(1000);
Response.ContentType = "text/event-stream";
Response.CacheControl = "no-cache";
Response.AddHeader("connection", "keep-alive");
var events = this.Session["Events"] as Dictionary<string, string>;
Response.Write($"event: message\n");
if (events != null && events.Count != 0)
{
foreach (KeyValuePair<string, string> message in events)
{
Response.Write($"data: {message.Key} has been {message.Value}\n\n");
}
}
Response.Flush();
Thread.Sleep(1000);
Response.Close();
}
Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.

Retrieving XML as downloadable attachment from MVC Controller action

I am rewriting an existing webform to use js libraries instead of using the vendor controls and microsoft ajax tooling (basically, updating the web app to use more contemporary methodologies).
The AS-IS page (webform) uses a button click handler on the server to process the submitted data and return a document containing xml, which document can either then be saved or opened (opening opens it up as another tab in the browser). This happens asynchronously.
The TO-BE page uses jquery ajax to submit the form to an MVC controller, where virtually the same exact code is executed as in the server-side postback case. I've verified in the browser that the same data is being returned from the caller, but, after returning it, the user is NOT prompted to save/open - the page just remains as if nothing ever happened.
I will put the code below, but I think I am just missing some key diferrence between the postback and ajax/controller contexts to prompt the browser to recognize the returned data as a separate attachment to be saved. My problem is that I have looked at and tried so many ad-hoc approaches that I'm not certain what I am doing wrong at this point.
AS-IS Server Side Handler
(Abridged, since the SendXml() method is what generates the response)
protected void btnXMLButton_Clicked(object sender, EventArgs e)
{
//generate server side biz objects
//formattedXml is a string of xml iteratively generated from each selected item that was posted back
var documentStream = MemStreamMgmt.StringToMemoryStream(formattedXml);
byte[] _documentXMLFile = documentStream.ToArray();
SendXml(_documentXMLFile);
}
private void SendXml(byte[] xmlDoc)
{
string _xmlDocument = System.Text.Encoding.UTF8.GetString(xmlDoc);
XDocument _xdoc = XDocument.Parse(_xmlDocument);
var _dcpXMLSchema = new XmlSchemaSet();
_dcpXMLSchema.Add("", Server.MapPath(#"~/Orders/DCP.xsd"));
bool _result = true;
try
{
_xdoc.Validate(_dcpXMLSchema, null);
}
catch (XmlSchemaValidationException)
{
//validation failed raise error
_result = false;
}
// return error message
if (!_result)
{
//stuff to display message
return;
}
// all is well .. download xml file
Response.ClearContent();
Response.Clear();
Response.ContentType = "text/plain";
Response.AddHeader("Content-disposition", "attachment; filename=" + "XMLOrdersExported_" + string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}.xml", DateTime.Now));
Response.BinaryWrite(xmlDoc);
Response.Flush();
Context.ApplicationInstance.CompleteRequest();
Response.End();
}
TO-BE (Using jquery to submit to a controller action)
Client code: button click handler:
queueModel.getXmlForSelectedOrders = function () {
//create form to submit
$('body').append('<form id="formXmlTest"></form>');
//submit handler
$('#formXmlTest').submit(function(event) {
var orderNbrs = queueModel.selectedItems().map(function (e) { return e.OrderId() });
console.log(orderNbrs);
var ordersForXml = orderNbrs;
var urlx = "http://localhost:1234/svc/OrderServices/GetXml";
$.ajax({
url: urlx,
type: 'POST',
data: { orders: ordersForXml },
dataType: "xml",
accepts: {
xml: 'application/xhtml+xml',
text: 'text/plain'
}
}).done(function (data) {
/*Updated per comments */
console.log(data);
var link = document.createElement("a");
link.target = "blank";
link.download = "someFile";//data.name
console.log(link.download);
link.href = "http://localhost:23968/svc/OrderServices/GetFile/demo.xml";//data.uri;
link.click();
});
event.preventDefault();
});
$('#formXmlTest').submit();
};
//Updated per comments
/*
[System.Web.Mvc.HttpPost]
public void GetXml([FromBody] string[] orders)
{
//same code to generate xml string
var documentStream = MemStreamMgmt.StringToMemoryStream(formattedXml);
byte[] _documentXMLFile = documentStream.ToArray();
//SendXml(_documentXMLFile);
string _xmlDocument = System.Text.Encoding.UTF8.GetString(_documentXMLFile);
XDocument _xdoc = XDocument.Parse(_xmlDocument);
var _dcpXMLSchema = new XmlSchemaSet();
_dcpXMLSchema.Add("", Server.MapPath(#"~/Orders/DCP.xsd"));
bool _result = true;
try
{
_xdoc.Validate(_dcpXMLSchema, null);
}
catch (XmlSchemaValidationException)
{
//validation failed raise error
_result = false;
}
Response.ClearContent();
Response.Clear();
Response.ContentType = "text/plain";
Response.AddHeader("Content-disposition", "attachment; filename=" + "XMLOrdersExported_" + string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}.xml", DateTime.Now));
Response.BinaryWrite(_documentXMLFile);
Response.Flush();
//Context.ApplicationInstance.CompleteRequest();
Response.End();
}
}*/
[System.Web.Mvc.HttpPost]
public FileResult GetXmlAsFile([FromBody] string[] orders)
{
var schema = Server.MapPath(#"~/Orders/DCP.xsd");
var formattedXml = OrderXmlFormatter.GenerateXmlForSelectedOrders(orders, schema);
var _result = validateXml(formattedXml.DocumentXmlFile, schema);
// return error message
if (!_result)
{
const string message = "The XML File(s) are not valid! Please check with your administrator!.";
return null;
}
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "blargoWargo.xml",
Inline = false
};
System.IO.File.WriteAllBytes(Server.MapPath("~/temp/demo.xml"),formattedXml.DocumentXmlFile);
return File(formattedXml.DocumentXmlFile,MediaTypeNames.Text.Plain,"blarg.xml");
}
[System.Web.Mvc.HttpGet]
public FileResult GetFile(string fileName)
{
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = fileName,
Inline = false
};
Response.AppendHeader("Content-Disposition", cd.ToString());
var fName = !string.IsNullOrEmpty(fileName)?fileName:"demo.xml";
var fArray = System.IO.File.ReadAllBytes(Server.MapPath("~/temp/" + fName));
System.IO.File.Delete(Server.MapPath("~/temp/" + fName));
return File(fArray, MediaTypeNames.Application.Octet);
}
UPDATE:
I just put the AS-IS/TO-BE side by side, and in the dev tools verified the ONLY difference (at least as far as dev tools shows) is that the ACCEPT: header for TO-BE is:
application/xhtml+xml, /; q=0.01
Whereas the header for AS-IS is
text/html, application/xhtml+xml, image/jxr, /
Update II
I've found a workaround using a 2-step process with a hyperlink. It is a mutt of a solution, but as I suspected, apparently when making an ajax call (at least a jQuery ajax call, as opposed to a straight XmlHttpRequest) it is impossible to trigger the open/save dialog. So, in the POST step, I create and save the desired file, then in the GET step (using a dynamically-created link) I send the file to the client and delete it from the server. I'm leaving this unanswered for now in the hopes someone who understands the difference deeply can explain why you can't retrieve the file in the course of a normal ajax call.

Ways to get data from localhost port using CefSharp

currently developing a .net C# application which is showing a web browser. But since visual studio web browser is still using ie7 and does not support quite lots of things, I plan to put in the CefSharp which is the Chromium. So, have you guys every try get some json data from a localhost server using CefSharp? I have tried two ways to get it but failed.
For C# in Visual Studio, I fired the Chromium browser like this:
var test = new CefSharp.WinForms.ChromiumWebBrowser(AppDomain.CurrentDomain.BaseDirectory + "html\\index.html")
{
Dock = DockStyle.Fill,
};
this.Controls.Add(test);
Then for the index.html, it is require to get data from local host port 1000 after it loaded. I have tried two ways for the javascript:
First using XMLHttpRequest:
var xmlhttp = new XMLHttpRequest();
var url = "http://localhost:1000/api/data1";
var services;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
services = jQuery.parseJSON(xmlhttp.responseText);
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
Secondly using jquery's .get():
$.get("http://localhost:1000/api/data1", function (data) {
var services = data;
});
But both ways can't return the data. If I put the index.html into normal browser like Chrome or Firefox, I am able to get the data.
Is it something missing in my coding? Any ideas what's wrong guys?
I am using Chromium web browser and making GET request to localhost for JSON. Along with this i am running a webserver which keeps on listening and return JSON.
Webserver:
public class WebServer
{
public WebServer()
{
}
void Process(object o)
{
Thread thread = new Thread(() => new WebServer().Start());
thread.Start();
HttpListenerContext context = o as HttpListenerContext;
HttpListenerResponse response = context.Response;
try
{
string json;
string url = context.Request.Url.ToString();
if (url.Contains("http://localhost:8888/json"))
{
List<SampleObject> list = new List<SampleObject>();
json = JsonConvert.SerializeObject(new
{
results = list
});
byte[] decryptedbytes = new byte[0];
decryptedbytes = System.Text.Encoding.UTF8.GetBytes(json);
response.AddHeader("Content-type", "text/json");
response.ContentLength64 = decryptedbytes.Length;
System.IO.Stream output = response.OutputStream;
try
{
output.Write(decryptedbytes, 0, decryptedbytes.Length);
output.Close();
}
catch (Exception e)
{
response.StatusCode = 500;
response.StatusDescription = "Server Internal Error";
response.Close();
Console.WriteLine(e.Message);
}
}
}
catch (Exception ex)
{
response.StatusCode = 500;
response.StatusDescription = "Server Internal Error";
response.Close();
Console.WriteLine(ex);
}
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public void Start()
{
HttpListener server = new HttpListener();
server.Prefixes.Add("http://localhost:8888/json");
server.Start();
while (true)
{
ThreadPool.QueueUserWorkItem(Process, server.GetContext());
}
}
}
public class SampleObject
{
string param1 { get; set; }
string param2 { get; set; }
string param3 { get; set; }
}
To Start Webserver:
Thread thread = new Thread(() => new WebServer().Start());
thread.Start();
Index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
$(document).ready(function(){
$.get("http://localhost:8888/json", function (data) {
var jsonData= data;
});
});
</script>
</head>
<body>
<p>Example JSON Request.</p>
</body>
</html>
Before launching Index.html inside Chromium web browser, start running webserver to listen for requests. After document load event it makes a ajax call, then it hits the Webserver then Webserver returns JSON. You can test it using Chrome also. Start webserver and type the URL(http://localhost:8888/json) in address bar you will see returned JSON in Developers tools.
Note: Code is not tested. Hope it will work.

Geolocation with Titanium

I want to develop an application with Appcelerator where I have a fleet of bikes, all with smartphone. I want to know the location of each one. Can someone give me a help by using localization by smartphone android GPS?
I'm assuming you want to get it periodically, so you can track where the bikes are at a given moment...
What you would need is to use Titanium Geolocation. you can read about it here: http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Geolocation
and call getCurrentPosition method (http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Geolocation-method-getCurrentPosition) that accepts a callback function with the location.
Inside that callback function send the information to your server (http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Network-method-createHTTPClient)
and just call that function every X interval you with using a setInterval or setTimeout.
keep in mind that the app will need to run in order for it to keep working, unless you write an android service (not possible on iOS).
I will suggest you to do like this:
first create service.js under android folder, and there write something like:
startSendigGpsPositions();
function startSendingGpsPosition(){
Ti.Geolocation.getCurrentPosition(function(e){
var postData = {};
postData["localizationSystems"] = [];
postData["localizationSystems"].push({
"localizationSystem_id":"gps",
"entries":[ {
"lat": e.coords.latitude,
"lon": e.coords.longitude,
"accuracy": e.coords.accuracy
} ]
});
callHttpFunction(postData, {
onSuccess: function(){
//if needed you can set a timeout so the battery does not die immediately
setTimeOut(function(){
startSendingGpsPosition();
}, 1000)
},
onError: function(){
// decide if you want to show the error to the user or just ignore it and recall startSendingGpsPosition
}
})
});
}
function callHttpFunction(postData, callback){
var xhr = Titanium.Network.createHTTPClient();
xhr.open(POST, url);
xhr.onload = function(e) {
var res = JSON.parse(this.responseText);
callback.onSuccess(res);
};
xhr.onerror = function(e) {
var res = JSON.parse(this.responseText);
callback.onError(res);
};
if (Titanium.Network.online) {
xhr.send(postData);
} else {
if (callback.error) { callback.onError(); }
};
}
From index.js you call it like this:
function startService(){
var intent = Ti.Android.createServiceIntent({
url : 'service.js'
});
if(!Ti.Android.isServiceRunning(intent)){
Ti.Android.startService(intent);
}
}
Another way is to create a native module where you use onLocationChanged function, so any time your location will change it will give you back the geo point without having to ask any 1 second for the gps position. This is how it should look like:
LocationListener locationListener = null;
...
locationListener = new LocationListener() {
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onProviderDisabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onLocationChanged(Location gpsLocation) {
HashMap<String , String> map = new HashMap<String, String>();
HashMap<String , String> container = new HashMap<String, String>();
map.put("success", "true");
container.put("latitude", ""+gpsLocation.getLatitude());
container.put("longitude", ""+gpsLocation.getLongitude());
container.put("accuracy", ""+gpsLocation.getAccuracy());
container.put("altitude", ""+gpsLocation.getAltitude());
container.put("provider", ""+gpsLocation.getProvider());
container.put("speed", ""+gpsLocation.getSpeed());
map.put("container", (new JSONObject(container)).toString());
callbackLocation.call(getKrollObject(), map);
}
};
Once you have the data in the callback you can do the http request from javascript :)
I'd recommend NOT using Titanium, especially if you plan to make another App. I started with Titanium and although I found Javascript straightforward and enjoyable, the framework is severely limiting when you start trying to do more advanced things. With native you have much greater control, and it's very much worth the hump of learning.

Categories

Resources