260 likes | 403 Views
Data access in native mobile apps. http://www.flickr.com/photos/ourcage/8343799386/. Options for data storage / access. Storing data locally On the actual device Typically in Flash memory Storing data remotely On the server Typically in datastore. Pros and cons of local vs remote.
E N D
Data access in native mobile apps http://www.flickr.com/photos/ourcage/8343799386/
Options for data storage / access • Storing data locally • On the actual device • Typically in Flash memory • Storing data remotely • On the server • Typically in datastore
Pros and cons of local vs remote • Pros of local storage (vs remote) • Data are readily accessible for per-user computation • Data are accessible even without network • Cons of local storage (vs remote) • Data are not readily accessible for sharing with other people, or combining with others' data • Data can easily be stolen from devices
Examples of situations to store locally • Storing user preferences • Storing username to reduce need for retyping • Storing records that are waiting to propagate back up to the server • Storing content in a local cache to improve performance • Storing rich content that is only needed locally
Examples of reasons to store remotely • Storing data for plotting all users' data on a shared map or other visualization • Storing data for aggregate analysis of trends • Storing data from one user in order to show it to other users (e.g., photo sharing) • Storing information that needs to be analyzed with a computationally expensive algorithm • Storing data for long-term archiving
Options for storing data locally • Property-value list • Every value is referenced by its property name • Files and blobs • Good for storing rich content • Structured data • Basically your own local database server
Saving properties: key functions • Ti.App.Properties.setString(propName, propValue); • Ti.App.Properties.removeProperty(propName); • varallProperties = Ti.App.Properties.listProperties().sort(); for(vari = 0; i < allProperties.length; i++) { …
Some caveats… • All app-defined properties will be loaded when the app starts up… • So don’t save hundreds and hundreds of properties, because that would slow startup. • System-defined properties will appear in the list of properties but cannot be edited reliably • System-defined properties might appear after execution begins (especially in simulator)
Saving files: key functions • To list and read files: vardirPath = Ti.Filesystem.getApplicationDataDirectory(); vardirObject = Ti.Filesystem.getFile(dirPath); varallFiles = dirObject.getDirectoryListing(); for(vari = 1; i < allFiles.length; i++) { varfilename = allFiles[i]; varfileObject = Ti.Filesystem.getFile(dirPath, filename); varfileContent = fileObject.read(); // string or blob • To store: fileObject.write(filevalue, false); • To delete: fileObject.deleteFile();
Other useful notes • One performance-related file will be automatically created by the emulator (Android) or simulator (iOS). • Other useful directories you can read/write: • Resources subdirectory (generally read-only) • External storage • Flash card • Android only • Check with isExternalStoragePresent first
Structured data • Essentially a mini SqlLite database • Embedded database server • Limited data types: • NULL, INTEGER, REAL, TEXT, BLOB • But really you can put any kind of data into any column (except for INTEGER primary keys) - it will be coerced • Primary operators: • =, !=, <, >, <=, >=, IN, NOT IN, BETWEEN • ORDER BY and GROUP BY are generally reliable • Nice built-in functions http://www.sqlite.org/lang_corefunc.html
Connecting to a database • First time you open() a database, it will be initialized. Use CREATE TABLE IF NOT EXISTS. Close connection after each operation (yes). // for example, let's make a table for storing preferences vardb = Ti.Database.open('mydb'); db.execute( 'CREATE TABLE IF NOT EXISTS proplist(pid '+ 'INTEGER PRIMARY KEY, pname TEXT, '+ 'pvalueTEXT)' );
Retrieving all rows in a table var items = []; var db = Ti.Database.open('mydb'); var rs = db.execute('SELECT * from proplist'); while(rs.isValidRow()) { var item = { pid : rs.fieldByName('pid'), pname : rs.fieldByName('pname'), pvalue : rs.fieldByName('pvalue') }; items.push(item); rs.next(); } db.close();
Insert, update, delete db.execute('INSERT INTO proplist'+ '(pname,pvalue) VALUES (?,?)', pname, pvalue); pid = db.lastInsertRowId; db.execute('UPDATE proplist SET pname=?, '+ 'pvalue=? WHERE pid=?', pname, pvalue, pid); db.execute('DELETE FROM proplist WHERE pid=?', pid);
Important considerations • Security • Databases are not encrypted: if you store private data, be sure to encrypt it first (to be discussed later on) • Performance • Indexes are not created automatically except for primary key; to create a custom index, see http://www.sqlite.org/lang_createindex.html • Usability • For maximal usability, explore how to use platform-specific APIs so your app matches the usual look and feel of the platform
Integrating remote content into native mobile apps • User opens mobile app • App calls up the server, sends URL • Server figures out how to handle request • Maybe just sends a file back • Maybe calls some custom code (JSP or servlet) • Server sends HTML/XML/JSON to app • App interprets data • User interacts with app and is happy
Big picture: Note similarity to AJAX in mobile web!! AppUI AppJS Server Servlet Data store UI event URL Request Query, etc Data HTML/XML/JSON HTML/XML/JSON updates
Very simple example: just get html varurl = "http://www.google.com/"; var client = Ti.Network.createHTTPClient({ // function called when the response data is available onload : function(e) { alert('success:'+this.responseText); }, // function called when an error occurs, including a timeout onerror : function(e) { alert('error'+e.error); }, timeout : 5000 /* in milliseconds */ }); client.open("GET", url); client.send(); Ti.UI.createWindow().open(); Make a servlet that returns some HTML Hit the URL
Debugging iOS apps locally on a Mac iOS apps in simulator often can’t access GAE. Here’s a hideous workaround. • Open Terminal (spotlight > Terminal) • Map 127.0.0.1 to a fake hostname vi /etc/hosts arrow key down to the end of the document, type letter “a” to append add the line: 127.0.0.1 www.cs496.com to save, type: escape :w! escape :q • Download Burp (or another proxy) proxy from http://portswigger.net/burp/downloadfree.html • Unzip Burp proxy, move files some place safe • Set Burp as your Mac’s proxy (Apple Symbol in top left corner > System preferences > Advanced > Proxies; check both Web Proxy and Secure Web Proxy, set the ip address to 127.0.0.1 and port to 8080) • Go to Terminal and start up Burp cd wherever you saved those Burp files java -jar -Xmx2g burpsuite_v1.4.01.jar • Now that Burp is running, go to your regular browser and try to hit http://www.cs496.com:8888/_ah/admin • By default, Burp will block access. Go to the Burp window, proxy tab, and click “Intercept” button to turn intercept off. Now the hostname should nicely map to your GAE admin console • In your Titanium code, you should now be able to successfully retrieve data from GAE through the fake hostname.
Debugging Android apps in emulator • Very simple: use 10.0.2.2 in your app to refer to your own computer from the emulator • NOTE: The instruction above is about the emulator. Accessing your computer from a physical device still requires accessing your computer through your computer's real IP.
Example: Getting HTML from GAE running locally (iOS) <%= (1+1) %> varurl = "http://10.0.2.2:8888/test1.jsp"; var client = Ti.Network.createHTTPClient({ // function called when the response data is available onload : function(e) { alert('success:'+this.responseText); }, // function called when an error occurs, including a timeout onerror : function(e) { alert('error'+e.error); }, timeout : 5000 /* in milliseconds */ }); client.open("GET", url); client.send(); Ti.UI.createWindow().open(); Make a servlet that returns some HTML In iOS app, create an http client, with specified “onload” and “onerror” handlers. Open, send request.
Getting fancier: posting datajust like a browser would Make a servlet that returns some HTML In app, create an http client, with specified “onload” and “onerror” handlers. Open, send POST request with parameters. you sent me a total of <%= Integer.parseInt(request.getParameter("a")) + Integer.parseInt(request.getParameter("b")) %> varurl = "http://10.0.2.2:8888/test2.jsp"; var client = Ti.Network.createHTTPClient({ // function called when the response data is available onload : function(e) { alert('success:'+this.responseText); }, // function called when an error occurs, including a timeout onerror : function(e) { alert('error'+e.error); }, timeout : 5000 /* in milliseconds */ }); client.open("POST", url); // use GET for idempotent, POST for non-idempotent operations client.send({a:1,b:2}); Ti.UI.createWindow().open();
Now you have some options… … for what your servlet does with the data… • Use it for some computations • Store it in the datastore • Forward it to another server (e.g., webservice)
And some options for what to send back… • HTML • Pros: No need to write a separate servlet • Cons: Very inefficient to send and parse HTML • XML • Pros: Compatible with many other systems • Cons: Moderately inefficient to send and parse • JSON • Pros: Highly efficient to send and parse • Cons: More work for you, in many cases
JS for reading XML in Titanium … onload : function(e) { var xml = this.responseXML; varels = xml.getElementsByTagName("myelement"); var sum = 0; for (var i = 0; i < els.length; i++) { var vl = parseFloat(els.item(i).getAttribute("cnt")); if (!isNaN(vl) && vl > 0) sum += vl; } alert("the sum is "+sum); }, …
JS for reading JSON in Titanium … onload : function(e) { Ti.API.info("Received text: " + this.responseText); varjson = null; try { json= JSON.parse(this.responseText); alert('success'+json); } catch (err) { alert('unable to parse json:'+this.responseText); } }, …