Setting up our Android Project
The time has finally come! We have everything setup for us to be able to interact with our MySQL remote database from within an Android application. So, let’s create it! In this tutorial we will develop the login and register features of our app.
**You should be familiar with the basics of setting up an Android projects and the structure of an Android project. If at any time you are confused,
Step 1: Creating a New Android Project
Even though Android Studios just came out, I’m going to use Eclipse to setup my new Android Project. I gave it the following credentials:
- Application Name: MySQLTest
- Project Name: MySQLTest
- Package Name: com.example.mysqltest
Step 2: Setting Up Classes and Layouts
Create the following Java classes and xml layouts:
Java files
- Login.java
- Register.java
- AddComment.java
- ReadComments.java
- JSONParser.java
Android XML Layouts
- login.xml
- register.xml
- add_comment.xml
- read_comments.xml
- single_comment.xml
Step 3: Set up the Permissions and the Manifest
In order for use to interact with our PHP scripts and our remote MySQL database, we will need to make sure our Android application has the internet permission. Since we are modifying our manifest, we might as well add all of the activities we are going to have in our application as well.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mysqltest"
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.mysqltest.Login"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.mysqltest.Register"
android:label="@string/app_name" >
</activity>
<activity
android:name="com.example.mysqltest.AddComment"
android:label="@string/app_name" >
</activity>
<activity
android:name="com.example.mysqltest.ReadComments"
android:label="@string/app_name" >
</activity>
</application>
</manifest>
Step 4: Creating Simple Login and Register Layouts
I’m not the best when it comes to designing xml layouts. Especially, when I’m creating tutorials, so please bare with me here, these are going to be some ugly layouts for now.
login.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/login"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/login"
android:layout_marginBottom="25dp"
android:text="Register" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/register"
android:layout_alignLeft="@+id/password"
android:layout_alignRight="@+id/password"
android:text="Login" />
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/login"
android:layout_centerHorizontal="true"
android:ems="10"
android:inputType="textPassword" >
<requestFocus />
</EditText>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="17dp"
android:gravity="center"
android:text="Android Remote Server Tutorial"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="bold" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView2"
android:layout_centerHorizontal="true"
android:src="@drawable/arrowstars" />
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/password"
android:layout_alignLeft="@+id/password"
android:layout_marginLeft="22dp"
android:text="Password" />
<EditText
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/TextView01"
android:layout_centerHorizontal="true"
android:ems="10" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/TextView01"
android:layout_centerVertical="true"
android:text="Username" />
</RelativeLayout>
register.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView2"
android:layout_centerHorizontal="true"
android:src="@drawable/arrowstars" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/password"
android:layout_centerVertical="true"
android:text="Username" />
<EditText
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:ems="10" />
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/username"
android:layout_below="@+id/username"
android:text="Password" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="Android Remote Server Tutorial"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="bold" />
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/TextView01"
android:layout_centerHorizontal="true"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/password"
android:layout_below="@+id/password"
android:text="Register" />
</RelativeLayout>
know I’m not using the standard string references for my text values, but don’t worry about that, we can fix everything later.
and Register Activities.
Now, the fun begins, we need to test our login system. First, let create a simple success page for our ReadComments.java and read_comments.xml
read_comments.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Success!"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
ReadComments.java
package com.example.mysqltest;
import android.app.Activity;
import android.os.Bundle;
public class ReadComments extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.read_comments);
}
}
That was simple enough! Now, we need to create a class that will help us parse some JSON information. There are a lot of JSON parsing classes for Android on the internet you can use, or you could create your own. To save time, I’m going to use one I found on the internet, and modify it to our needs.
package com.example.mysqltest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
public JSONObject getJSONFromUrl(String url) {
// Making HTTP request
try {
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
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 reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
This JSONParser.java class may look like a bunch of baloney to you right now, but this is the class that allows us to interpret the JSON data our php web service spits out. With this class we will be able to read whether or not a login was successful (with either a 0 or 1). We will also be able to list out all of our posts in an orderly fashion using this JSON parser. We will go over the details later. As for now lets setup our Login Activity:
Login.java v0.1 – Basics
package com.example.mysqltest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class Login extends Activity implements OnClickListener{
private EditText user, pass;
private Button mSubmit, mRegister;
// Progress Dialog
private ProgressDialog pDialog;
// JSON parser class
JSONParser jsonParser = new JSONParser();
//php login script location:
//localhost :
//testing on your device
//put your local ip instead, on windows, run CMD > ipconfig
//or in mac's terminal type ifconfig and look for the ip under en0 or en1
// private static final String LOGIN_URL = "http://xxx.xxx.x.x:1234/webservice/login.php";
//testing on Emulator:
private static final String LOGIN_URL = "http://10.0.2.2:1234/webservice/login.php";
//testing from a real server:
//private static final String LOGIN_URL = "http://www.yourdomain.com/webservice/login.php";
//JSON element ids from repsonse of php script:
private static final String TAG_SUCCESS = "success";
private static final String TAG_MESSAGE = "message";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
//setup input fields
user = (EditText)findViewById(R.id.username);
pass = (EditText)findViewById(R.id.password);
//setup buttons
mSubmit = (Button)findViewById(R.id.login);
mRegister = (Button)findViewById(R.id.register);
//register listeners
mSubmit.setOnClickListener(this);
mRegister.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// determine which button was pressed:
switch (v.getId()) {
case R.id.login:
new AttemptLogin().execute();
break;
case R.id.register:
Intent i = new Intent(this, Register.class);
startActivity(i);
break;
default:
break;
}
}
//AsyncTask is a seperate thread than the thread that runs the GUI
//Any type of networking should be done with asynctask.
class AttemptLogin extends AsyncTask{
//three methods get called, first preExecture, then do in background, and once do
//in back ground is completed, the onPost execture method will be called.
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... args) {
return null;
}
protected void onPostExecute(String file_url) {
}
}
}
This java file looks pretty basic so far. The important part is that we are using AsyncTask because connecting our php scripts may take some time, and therefore we don’t want to use the same thread that runs the GUI of our application. If we did, it would really slow things down, freeze usability for our user, and maybe even crash our application. AsyncTask will create another thread to execute the tasks we want completed.
You also want to note where your PHP scripts are located. If you are using Xampp as a local server, make sure you uncomment the appropriate LOGIN_URL. If your scripts are on a shared server somewhere, use your domain name as the LOGIN_URL.
Here is the break down of what we want our login java class to do:
If the register button is clicked, open the register activity.
If the login button is clicked, create a dialog that pops up before trying to connect to the scripts.
Once the dialog is displayed, get the inputs from our “username” and “password” EditTexts.
POST that data to our URL that handles logging in.
Retrieve the JSON response from our server.
Interpret the response.
If the response element “success” is 1, finish the Login Activity, and open the ReadComments Activity.
If the response element “success” is 0, do nothing.
In either case, we will close our dialog.
Lastly, we will display the JSON response for the element “message” as a toast.<
Awesome, let’s make it happen.
Login.java v0.1 – Basics
Reading JSON data is as simple as that! We simple POST the information to the url we want within the make HTTP request, and we parse the result! You can now look at the JSONParser.java class and see that it is quite a simple class. All that it does is determine what type of method to use, either POST or GET, and then it makes a httprequest and gets the response. Lastly, the JSONParser class will use a buffer reader to get the different elements of the JSON object.
You also may have noticed that the we are calling a method from JSONParser class called makeHttpRequest. This is a method that will read the JSON data an put everything in the appropriate list. Let’s add modify our JSONParser class to include this method.
JSONParser.java
package com.example.mysqltest;
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 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.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
public JSONObject getJSONFromUrl(final String url) {
// Making HTTP request
try {
// Construct the client and the HTTP request.
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
// Execute the POST request and store the response locally.
HttpResponse httpResponse = httpClient.execute(httpPost);
// Extract data from the response.
HttpEntity httpEntity = httpResponse.getEntity();
// Open an inputStream with the data content.
is = httpEntity.getContent();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
// Create a BufferedReader to parse through the inputStream.
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
// Declare a string builder to help with the parsing.
StringBuilder sb = new StringBuilder();
// Declare a string to store the JSON object data in string form.
String line = null;
// Build the string until null.
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
// Close the input stream.
is.close();
// Convert the string builder data to an actual string.
json = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// Try to parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// Return the JSON Object.
return jObj;
}
// function get json from url
// by making HTTP POST or GET mehtod
public JSONObject makeHttpRequest(String url, String method,
Listparams) {
// Making HTTP request
try {
// check for request method
if(method == "POST"){
// request method is POST
// defaultHttpClient
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();
}else if(method == "GET"){
// request method is GET
DefaultHttpClient httpClient = new DefaultHttpClient();
String paramString = URLEncodedUtils.format(params, "utf-8");
url += "?" + paramString;
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
Another thing I wanted to mention is that, we don’t want to create a toast within the “doInBackground” method of our AsyncTask class, instead, we want to pass the response to the onPostExecute method and display the toast there! Again, this toast is displaying whatever the “message” JSON element contains, and therefore, as a front-end Android developer, you wouldn’t have to worry about what it says since that would be the back-end programmer’s job. As you can see here, this is what is displayed when you try to login with a unregistered username:
Now, let’s outline what we want the Register.java class should do:
Try to register user.
If JSON element “success” is 1, display success message and close registration activity.
If JSON element “success” is 0, display message explaining why registration is unsuccessful.
That’s it.
Register.java
package com.example.mysqltest;
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.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class Register extends Activity implements OnClickListener{
private EditText user, pass;
private Button mRegister;
// Progress Dialog
private ProgressDialog pDialog;
// JSON parser class
JSONParser jsonParser = new JSONParser();
//php login script
//localhost :
//testing on your device
//put your local ip instead, on windows, run CMD > ipconfig
//or in mac's terminal type ifconfig and look for the ip under en0 or en1
// private static final String LOGIN_URL = "http://xxx.xxx.x.x:1234/webservice/register.php";
//testing on Emulator:
private static final String LOGIN_URL = "http://10.0.2.2:1234/webservice/register.php";
//testing from a real server:
//private static final String LOGIN_URL = "http://www.yourdomain.com/webservice/register.php";
//ids
private static final String TAG_SUCCESS = "success";
private static final String TAG_MESSAGE = "message";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.register);
user = (EditText)findViewById(R.id.username);
pass = (EditText)findViewById(R.id.password);
mRegister = (Button)findViewById(R.id.register);
mRegister.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new CreateUser().execute();
}
class CreateUser extends AsyncTask{
/**
* Before starting background thread Show Progress Dialog
* */
boolean failure = false;
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Register.this);
pDialog.setMessage("Creating User...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
@Override
protected String doInBackground(String... args) {
// TODO Auto-generated method stub
// Check for success tag
int success;
String username = user.getText().toString();
String password = pass.getText().toString();
try {
// Building Parameters
Listparams = new ArrayList ();
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", password));
Log.d("request!", "starting");
//Posting user data to script
JSONObject json = jsonParser.makeHttpRequest(
LOGIN_URL, "POST", params);
// full json response
Log.d("Login attempt", json.toString());
// json success element
success = json.getInt(TAG_SUCCESS);
if (success == 1) {
Log.d("User Created!", json.toString());
finish();
return json.getString(TAG_MESSAGE);
}else{
Log.d("Login Failure!", json.getString(TAG_MESSAGE));
return json.getString(TAG_MESSAGE);
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
// dismiss the dialog once product deleted
pDialog.dismiss();
if (file_url != null){
Toast.makeText(Register.this, file_url, Toast.LENGTH_LONG).show();
}
}
}
}
The registration activity looks pretty similar to the login activity. All we do, is try to register a user by POSTing data to our registration script, if it is successful, cool. If not, display a message explaining why.