Tuesday, December 27, 2011

On the Third Day of PhoneGapping: Getting Data from a Server

Well your application would be pretty useless if you couldn't get data from a remote server now wouldn't it? Luckily since your PhoneGap application is running off of the file:// protocol it isn't limited by the same origin policy. This means we can request data using XmlHttpRequest from any domain.

I'm going to give you an example of searching for all tweets that mention PhoneGap that demonstrates this ability without the use of any extra JavaScript library like jQuery or Dojo.



So that is the example in a nutshell. It isn't very different from your normal XHR call except for one line that I need to bring to your attention:
if (request.status == 200 || request.status == 0) {
Most of the time you are just looking for the 200 status code you also need to accept the status code of 0 as also OK. Sometimes when you are requesting data via XHR from the file:// protocol you will get a 0 status code. As I said that is perfectly normal and you should treat it as a 200 and move on with your application.

Much of the rest of this code is just building up a HTML string I can do an insert into a div I've set aside for displaying the tweets. Just wanted to show everyone how easy it is to communicate with a remote server.

22 comments:

Bee said...

Hi Simon,

Thank you for your post. I have tried it and it works great!

Can you please tell me is it possible to make a POST request to a SOAP web service on Android? I tried, but I succeeded only on iOS. I have found this link
https://groups.google.com/forum/?fromgroups#!topic/android-developers/d-uq4ZSvSJU
where it is written that there is constraint about making ajax calls from android web view. Is this the case really or am I missing something?

Simon MacDonald said...

@Bee

There shouldn't be any problems doing an ajax request from the Android WebView. There are no cross origin request restrictions when running from the file:// protocol in the WebView. What problems are you having with the SOAP request?

Bee said...

This is my code:
var xmlHttp;

var message = constructMessage(functionName, paramsObject);

try {
xmlHttp = new XMLHttpRequest();
} catch (e) {
alert("There was problem making the request." + e.message);
return false;
}

xmlHttp.onload = function() {
if (xmlHttp.readyState == 4) {
if (xmlHttp.status == 200 || xmlHttp.status == 0) {
handleResponse(xmlHttp);
}
}
};

xmlHttp.onerror = function(e) {
alert("Error ocurred. Error = " + e.message);
};
xmlHttp.ontimeout = function(e) {
alert("Timeout error!");
};

xmlHttp.open('POST', 'http://domain:port/MobileService?wsdl', true);
xmlHttp.setRequestHeader('Content-Type', 'text/xml');
xmlHttp.setRequestHeader('SOAPAction', 'http://namespace/functionName');
xmlHttp.send(message);

This code works perfect on iOS and on android device (found that yesterday, I had not tested it on device when I wrote to your blog), but it still just hangs on the emulator.
I have set the permission for the INTERNET in the AndroidManifest.xml file.
LogCat says nothing. Just when I click the button to send the request the whole application blocks.
I am testing this on android2.2 device and avd.

Bee said...

Hello Simon,

I managed to make a call from an Android device, but it works kind of strange. The first time I run the application and click the send button nothing happens. When I close the application and then run it again everything seems to be fine. It turned out to be the "onDeviceReady" function not triggered the first time. After that (2,3 and so on times) the "onDeviceReady" function is called. Is this a known issue and how can I solve the problem?

Bee said...

For those that are experiencing the same issue as me:
I figured out that the "deviceReady" problem is connected with the emulator/device Android version. When I ran the code on 4.0 everything worked fine. Event was not triggered on 2.2 and 2.3.5.
I am using Phonegap 1.6.1.

@Simon
Is there a workaround for this if I am targeting devices with Android 2.2 ?
Sorry for the spam, I think the issue ran out of the blog post theme. Maybe you can link me to the right place for me to read?

Bee said...

I forgot to tell that I am using JQtouch. And finally it is the one causing all my problems. Sorry for accusing Android and Phonegap (blush). When I initialize the jqtouch before onload event deviceReady is not fired. When I move it in deviceReadyHandler it is fired but this is causing other issues with the layout.

New Micro said...

Using pure XMLHttpRequest was the only way I could solve the problem of $.ajax calls (JQuery Ajax) erroring out on Android only, regardless of everything else I've tried (and I've spent over 6 hours on it).

However, after going pure, the app stopped working on IOS, which is due to the status return 0 (I was only checking for status 200). So, thanks!

Unknown said...

Thanks Simon. It took me ages to get this to work (in iOS), but then discovered I needed to add '*.twitter.com' as an external host in the 'cordova.plist' file. Other newbies may find this hint useful!

dabbler said...

Hi, I tried this in phonegap 2.2 for gingerbread:
var request = new XMLHttpRequest();

request.open("GET", "http://search.twitter.com/search.json?q=phonegap", true);
request.onreadystatechange = function() {//Call a function when the state changes.
if (request.readyState == 4) {
if (request.status == 200 || request.status == 0) {
alert(request.responseText);
}
}
}
request.send();

but I am getting a status of 0 always and a text response null.
Can you please shed some light on this.
From my phone I can execute post commands via form submit but don't receive any data from server while if I do a post or get from the browser or android emulator 4.2 I get a proper json response. I have been looking for this for a long time. Please suggest.
Thanks

Simon MacDonald said...

@dabbler

I ran that code unaltered on 3 phones and it worked on all of them. Not sure what is going on with your app. What do you have in the white list?

Unknown said...

What about loading a local file through ajax?

Simon MacDonald said...

@Costin Popescu

See:

1) Loading from the assets dir - http://simonmacdonald.blogspot.ca/2011/12/on-fourth-day-of-phonegapping-creating.html
2) Also my XhrFileReader - http://simonmacdonald.blogspot.ca/2013/02/phonegap-android-xhrfilereader.html

As long as you have the file:// path correct you will be able to load the file via XHR.

aji said...

This code is note working in android4.1.2 . Can you tell me the white list settings..

Simon MacDonald said...

@Unknown

It is working just fine for me. You can try allowing everything in the whitelist first:

"access origin=".*"/"

and then pair it down to:

http://search.twitter.com
https://search.twitter.com

You'll need to add both as twitter now redirects to the https version. As well I've notice the service is currently over capacity.

aji said...

Sir resposetext am getting is null. What i should do?

Simon MacDonald said...

@aji

What url are you hitting? Did you white list it?

Anneleen said...

Hi Simon, I tried using XHR to load my data in Phonegap, but as I'm not a pro, I'm not sure I'm doing this right. You can find my code here: http://collabedit.com/3g3un

Can you give me some advice on it?

Simon MacDonald said...

@Anneleen

I made some edits on the link you sent. It looks like you forgot to parse the incoming data and there were some JS syntax errors.

Anneleen said...

Hi Simon, I tried with your correction, but still no luck. This is what I see in my logcat:

07-19 00:25:28.169: D/Cordova(1711): onPageFinished(file:///android_asset/www/index.html#/android_asset/www/vb.html)
07-19 00:25:28.169: D/DroidGap(1711): onMessage(onNativeReady,null)
07-19 00:25:28.169: D/DroidGap(1711): onMessage(onPageFinished,file:///android_asset/www/index.html#/android_asset/www/vb.html)

Simon MacDonald said...

@Anneleen

Well, "file:///android_asset/www/index.html#/android_asset/www/vb.html" doesn't look like a valid URL. What link do you click on to navigate to that page?

Anneleen said...

Normally it links to a html file in the assets/www folder called vb.html.

Anneleen said...

Found it! Forgot to add data-ajax="false" ^^