This tutorial will explain communicating with an external data source via an Android application, to be used when logging into a web based service or backing up data to an external source, among other things.
The external data can be stored in any of the many databases available today, which in turn can be hosted by any of the operating systems available. With this in mind, the theory becomes as important as the code.
We want to be able to send information from one platform to another completely separate platform and vice versa. It sounds simple, but there’s a language barrier to overcome and there’s not always an existing solution. We need a data type that both platforms can work with and some code in the middle to handle the requests. This is the API. The below diagram illustrates this better than words.
For this tutorial, I will be using a standard LAMP stack. The external database will be MySQL, and I will use PHP to make the API. The PHP file will write and read to the MySQL database based on requests received from the application. It will also send the results of the requests back to the app. The requests and responses will be in JSON as both parties understand this format. The goal is to enter a person’s name from the Android application and retrieve the person’s age from the external database. I know it’s simple, but it’s a broad area and it’s a good way to illustrate the process.
Note: I’m using a LAMP stack because it’s free and accessible. I will give further instructions in the download code about setting one up for those that require it, but I won’t be going into great detail about this side. I will show the code and explain its function, but it would be a very long read to go into detail about PHP, SQL, JSON and so forth.Also I am not touching upon security in this tutorial, I will most likely cover it separately in a follow up.
Creating the MySQL Database and Table
Connect to your MySQL instance however you prefer and perform the following.
create database android_remoteDB use android_remoteDB
create table users( _id int(10) primary key auto_increment, user_name varchar(25) not null unique, user_age int(2) not null );
Insert the Test Data
insert into users (user_name,user_age) values ("green ranger",27); insert into users (user_name,user_age) values ("red ranger",24); insert into users (user_name,user_age) values ("blue ranger",23); insert into users (user_name,user_age) values ("black ranger",29); insert into users (user_name,user_age) values ("yellow ranger",22); insert into users(user_name,user_age) values ("pink ranger",21);
Create the API
For the purpose of this tutorial, I will be using a single PHP file called android_api.php. This needs to be placed somewhere where it can be accessed via HTTP request. It might be in the www directory on a local LAMP or WAMP setup, or on a hosted server that you can access.
The app will send a tag to this page along with any additional parameters. The API will perform an action based on the content of the tag. In this basic example there is only one tag, one parameter, and one action, so it wasn’t really necessary. Still, doing it like this will help you understand how to perform multiple tasks from the API.
Note: In practice you may well split things across several PHP files, so you might have one file for config parameters, another to handle database requests, and so on. In the interest of keeping things both simple and quick, I will stick with a single file.<?php // check that the tag is present if (isset($_POST['tag']) && $_POST['tag'] != '') { $tag = $_POST['tag']; // Create a connection the the database and table $error = 'Could not connect to the database'; //Depending on your setup you might need to change the below for your own credentials mysql_connect('localhost','root','') or die ($error); mysql_select_db('remote_db') or die ($error); // check if tag = 'getAge' if ($tag == 'getAge') { //Get name $name = mysql_real_escape_string($_POST['name']); //Select records from the database $find = mysql_query("SELECT * FROM users WHERE user_name='$name'"); //check that records exist if (mysql_num_rows($find)>0) { while ($find_row = mysql_fetch_assoc($find)) { //Get user age $age = $find_row['user_age']; } //Return the response as a success, including age. $response["success"] = 1; $response["user"]["name"] = $name; $response["user"]["age"] = $age; echo json_encode($response); } else { //Return error $response["success"] = 0; $response["error"] = 1; $response["error_msg"] = "User could not be found"; echo json_encode($response); } } else { echo "No action for the tag"; } } else { echo "Unknown error"; } ?>
Creating the Application
In the IDE, create a new Android project by going to File → New → Android Application Project. Next, name the project. I went with remoteDB. From here on out, you can get away with clicking next on the screens you’re presented with.
Creating the JSON Class
To create a new class, go to your new project and open the src folder. Right click on the package, expand new and select class. Make sure to name it JSONParser.
The class itself is a fairly generic and widely used one. It makes a post request to the provided URL and converts the response to JSON format.
Next, open the class and insert the following.
package com.example.remotedb; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.List; import android.util.Log; public class JSONParser { static InputStream is = null; static JSONObject json = null; static String outPut = ""; // constructor public JSONParser() { } public JSONObject getJSONFromUrl(String url, List<NameValuePair> params) { // Making the HTTP request try { DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); httpPost.setEntity(new UrlEncodedFormEntity(params)); HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); is = httpEntity.getContent(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { BufferedReader in = new BufferedReader(new InputStreamReader( is, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = in.readLine()) != null) { sb.append(line + "\n"); } is.close(); outPut = sb.toString(); Log.e("JSON", outPut); } catch (Exception e) { Log.e("Buffer Error", "Error converting result " + e.toString()); } try { json = new JSONObject(outPut); } catch (JSONException e) { Log.e("JSON Parser", "Error parsing data " + e.toString()); } // return JSON String return json; } }
Creating the Layout File
Open up the activity_main.xml file in Res → Layout…
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"><!-- User Name Label --><TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="User Name:" android:padding="10dip" android:textColor="#33B5E5" /><!-- User Name Text Field --><EditText android:id="@+id/Enter" android:hint="Please Enter a Name" android:layout_width="fill_parent" android:layout_height="wrap_content" /><!-- Text View to display the returned age or a error message --><TextView android:id="@+id/Results" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="User Age:" android:textColor="#99CC00" android:padding="10dip" android:textStyle="bold"/><!-- Button to fire the getAge Method--><Button android:id="@+id/GetAge" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dip" android:text="Get Age" /></LinearLayout>Note: It’s normally the best practice to have your text in the strings.xml file.
Main Activity
Now for the main activity, which is in Src → Packagename → MainActicity.java…
Again, this is fairly simple. We have a method called getAge, which is called when the button is clicked. It passes paramaters such as URL and name over to the JSONParser and creates a JSON object from the response. This is then read and either the age or an error message is displayed in the results text view depending on whether the query was a success or not.
package com.example.remotedb; import java.util.ArrayList; import java.util.List; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { private JSONParser jsonParser; //10.0.2.2 is the address used by the Android emulators to refer to the host address // change this to the IP of another host if required private static String ageURL = "http://10.0.2.2/android_api.php"; private static String getAge = "getAge"; private static String jsonResult = "success"; String uname; String age_res; TextView Results; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Invoke the Json Parser jsonParser = new JSONParser(); final EditText Enter = (EditText) findViewById(R.id.Enter); Results = (TextView) findViewById(R.id.Results); Button GetAge = (Button) findViewById(R.id.GetAge); //Get Age Button GetAge.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { //Get the contents of the edit text uname = Enter.getText().toString(); //Pass the name to the JSON method and create a JSON object from the return JSONObject json = getAge(uname); // check the success of the JSON call try { if (json.getString(jsonResult) != null) { Results.setText(""); String res = json.getString(jsonResult); if(Integer.parseInt(res) == 1){ //If it's a success create a new JSON object for the user element JSONObject json_user = json.getJSONObject("user"); //Set the results text to the age from the above JSON object Results.setText("User Age: " + json_user.getString("age")); }else{ //If the user could not be found Results.setText("User could not be found"); } } } catch (JSONException e) { e.printStackTrace(); } } }); } //The below passes the tag and the user name over to the JSON parser class public JSONObject getAge(String name){ List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("tag", getAge)); params.add(new BasicNameValuePair("name", name)); JSONObject json = jsonParser.getJSONFromUrl(ageURL, params); return json; } }
Add the Internet Permission
One requirement of the app is the android.permission.INTERNET permission. This needs to be added to the AndroidManifest.xml file.
The line is:
uses-permission android:name="android.permission.INTERNET"
When added, the manifest should look something like the code below.
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.remotedb" android:versionCode="1" android:versionName="1.0" ><uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /><uses-permission android:name="android.permission.INTERNET" /><application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" ><activity android:name="com.example.remotedb.MainActivity" android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
You should be good to go at this stage. Try running the application.
Conclusion
Yes, the end result is extremely underwhelming. We’ve made a number appear on the screen. We have actually done much more than that, though. We’ve taken information from one platform and sent it to a completely foreign platform. The foreign platform was able to perform a task based on this information and to retrieve information from a database which the original platform had no knowledge of or possible access to. The second platform was able to send information back to the original which was then able to display the information to the user.
When it’s put like that, it sounds pretty cool and infinitely useful! You can see how such techniques allow multiple platforms to share data, and how different data sources can be combined.
Simple text objects such as JSON are very similar to morse code. They transcend other languages and can be universally understood. This makes them extremely useful and versatile. It’s the simple things that often solve the complex problems.