You are on page 1of 40

Android Application Development Training Tutorial

For more info visit http://www.zybotech.in

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Most all of the Android examples and tutorials out there assume you want to create and populate your database at runtime and not to use and access an independent, preloaded database with your Android application. The method I'm going to show you takes your own SQLite database file from the "assets" folder and copies into the system database path of your application so the SQLiteDatabase API can open and access it normally. 1. Preparing the SQLite database file. Assuming you already have your sqlite database created, we need to do some modifications to it. If you don't have a sqlite manager I recommend you to download the opensource SQLite Database Browser available for Win/Linux/Mac. Open your database and add a new table called "android_metadata", you can execute the following SQL statement to do it: CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US') Now insert a single row with the text 'en_US' in the "android_metadata" table: INSERT INTO "android_metadata" VALUES ('en_US') Then, it is necessary to rename the primary id field of your tables to "_id" so Android will know where to bind the id field of your tables. You can easily do this with SQLite Database Browser by pressing the edit table button , then selecting the table you want to edit and finally selecting the field you want to rename. After renaming the id field of all your data tables to "_id" and adding the "android_metadata" table, your database it's ready to be used in your Android application.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Modified database

Note: in this image we see the tables "Categories" and "Content" with the id field renamed to "_id" and the just added table "android_metadata". 2. Copying, opening and accessing your database in your Android application. Now just put your database file in the "assets" folder of your project and create a Database Helper class by extending the SQLiteOpenHelperclass from the "android.database.sqlite" package. Make your DataBaseHelper class look like this: public class DataBaseHelper extends SQLiteOpenHelper{ //The Android's default system path of your application database. private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/"; private static String DB_NAME = "myDBName"; private SQLiteDatabase myDataBase;

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

private final Context myContext; /** * Constructor * Takes and keeps a reference of the passed context in order to access to the application assets and resources. * @param context */ public DataBaseHelper(Context context) { super(context, DB_NAME, null, 1); this.myContext = context; } /** * Creates a empty database on the system and rewrites it with your own database. * */ public void createDataBase() throws IOException{ boolean dbExist = checkDataBase(); if(dbExist){ //do nothing - database already exist }else{ //By calling this method and empty database will be created into the default system path //of your application so we are gonna be able to overwrite that database with our database. this.getReadableDatabase(); try { copyDataBase(); } catch (IOException e) { throw new Error("Error copying database"); } } } /** * Check if the database already exist to avoid re-copying the file each time you open the application. * @return true if it exists, false if it doesn't */ private boolean checkDataBase(){ SQLiteDatabase checkDB = null; try{ String myPath = DB_PATH + DB_NAME; checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY); }catch(SQLiteException e){ //database does't exist yet. }

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

if(checkDB != null){ checkDB.close(); } return checkDB != null ? true : false; } /** * Copies your database from your local assets-folder to the just created empty database in the * system folder, from where it can be accessed and handled. * This is done by transfering bytestream. * */ private void copyDataBase() throws IOException{ //Open your local db as the input stream InputStream myInput = myContext.getAssets().open(DB_NAME); // Path to the just created empty db String outFileName = DB_PATH + DB_NAME; //Open the empty db as the output stream OutputStream myOutput = new FileOutputStream(outFileName); //transfer bytes from the inputfile to the outputfile byte[] buffer = new byte[1024]; int length; while ((length = myInput.read(buffer))>0){ myOutput.write(buffer, 0, length); } //Close the streams myOutput.flush(); myOutput.close(); myInput.close(); } public void openDataBase() throws SQLException{ //Open the database String myPath = DB_PATH + DB_NAME; myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY); } @Override public synchronized void close() { if(myDataBase != null) myDataBase.close(); super.close(); } @Override public void onCreate(SQLiteDatabase db) {

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

} @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } // Add your public helper methods to access and get content from the database. // You could return cursors by doing "return myDataBase.query(....)" so it'd be easy // to you to create adapters for your views. } That's it. Now you can create a new instance of this DataBaseHelper class and call the createDataBase() and openDataBase() methods. Remember to change the "YOUR_PACKAGE" to your application package namespace (i.e: com.examplename.myapp) in the DB_PATH string. ...

DataBaseHelper myDbHelper = new DataBaseHelper(); myDbHelper = new DataBaseHelper(this); try { myDbHelper.createDataBase(); } catch (IOException ioe) { throw new Error("Unable to create database"); } try { myDbHelper.openDataBase(); }catch(SQLException sqle){ throw sqle; } ...

Android Database Tutorial


4 August, 2010 (04:05) | Database | By: Randall Mitchell

Getting Started

This tutorial will cover the basics of using the SQLite database tools in Android. I am going to try to organize this tutorial so that it is easy to navigate, for myself and others, to use as a reference. At the same time, I will try to make it long enough so that people needing the extra help will be able to read through the entire tutorial to gain understanding on how to use databases in Android. Finally, I did some pretty heavy commenting in the code. You can jump straight to that if you prefer to read through just the code and comments. Lets get started.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Android Database Tools


The Android SDK provides two basic tools that assist you in writing basic programs that access a database in the Android environment. The first tool is SQLiteOpenHelper which is responsible for creating, opening, and upgrading a programs database. The other tool is SQLiteDatabase which is responsible for communicating changes to the data within the database. Once you know how to use these two classes, you should be able to write programs that create and communicate with an Android database. In this tuturial, I will first go about creating a database manager class that can be modified and plugged into most apps. This program will make use of both the SQLiteOpenHelper and the SQLiteDatabase classes. Afterwords, we will create a simple application that will instantiate and use our new database manager class.

Setting Up the SQLiteOpenHelper for Use


How our classes are layed out
The first thing we are going to do in this tutorial is extend the SQLiteOpenHelper class so that it creates our database with the appropriate structure that our application needs. The SQLiteOpenHelper class is abstract. This means we need to extend the class with a concrete class, overriding the appropriate methods. We also need to make available to this class a database name, table name, and column names. The best way to handle the variables is with constants. Constants ensure that the correct names are always used. I am going to make the SQLiteOpenHelper class internal internal to our database manager class. All of database constants will be inside the database manager class so they are accessible from both our SQLiteOpenHelper class and our main database class. This setup may not be common practice; but, I find it easier to work with. The SQLDatabase object, responsible for communicating with the database, is also going to be in the database manager class. This way all of our resources are available througout the manager class.

How our database is structured


For the purpose of this example I am using a single table with three columns: one column is the row id ( id), and the other two columns are Strings (table_row_one and table_row_two). All of these column names as well as a database version number (more on database version later) are going to be constants. Here is the code for what I have explained so far: our base class containing database constants, a SQLDatabase object, and an internal class that extends SQLiteOpenHelper.

AABDatabaseManager.java
import android.database.sqlite.SQLiteOpenHelper; public class AABDatabaseManager { private SQLiteDatabase db; // a reference to the database manager class. private final String DB_NAME = "database_name"; // the name of our database private final int DB_VERSION = 1; // the version of the database // the names for our private final String private final String private final String private final String database columns TABLE_NAME = "database_table"; TABLE_ROW_ID = "id"; TABLE_ROW_ONE = "table_row_one"; TABLE_ROW_TWO = "table_row_two";

// TODO: write the constructor and methods for this class // the beginnings our SQLiteOpenHelper class private class CustomSQLiteOpenHelper extends SQLiteOpenHelper { // TODO: override the constructor and other methods for the parent class

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

} } Now lets customize the helper class to create our database according to specification.

Overriding the SQLiteOpenHelper methods.


SQLiteOpenHelper has to know how to design the database. We tell it how by overriding the onCreate() method with the proper code for creating the database. Its as simple as creating a SQLite query string and passing it to the SQLiteDatabase object (db). SQLite query structure is out of scope for discussion here so I will not go into detail here. The SQLite query String needs to read: create table database_table ( id integer primary key autoincrement not null, table_row_one text, table_row_two text); Our method override with the query String built from constants looks like this: @Override public void onCreate(SQLiteDatabase db) { // the SQLite query string that will create our 3 column database table. String newTableQueryString = "create table " + TABLE_NAME + " (" + TABLE_ROW_ID + " integer primary key autoincrement not null," + TABLE_ROW_ONE + " text," + TABLE_ROW_TWO + " text" + ");"; // execute the query string to the database. db.execSQL(newTableQueryString); } We need to override the constructor as well. We pass the super constructor the context, the database name constant,null, and the database version constant. The null argument has to do with a custom CursorFactory. The CursorFactory is them creates the containers in which our data is passed out of the database. When data is collected from the database, it comes in the form of a Cursor. Cursors are a form of Java collection similar to an ArrayList. We are using the default CursorFactory so we can just pass in null. We will discuss the default Cursor later. Here is our constructor override: public CustomSQLiteOpenHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } There is only one other method that is required to be overridden in this class. The onUpdate() method is where you would include the programming required to revise the structure of the database. I am going to consider database revision to be out of scope for this tutorial so the override method will be left blank as follows: @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // NOTHING TO DO HERE. THIS IS THE ORIGINAL DATABASE VERSION. // OTHERWISE, YOU WOULD SPECIFIY HOW TO UPGRADE THE DATABASE // FROM OLDER VERSIONS. } That is pretty much it for extending the SQLiteOpenHelper class. Just a couple of overrides and our database is set for creation. Lets move on to creating our database manager class.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Creating the Database Manager Class


In the previous section we extended the class that is responsible for creating and updating the database. We also created a SQLiteDatabase object and some database constants. All of this was wrapped in the beginnings of a database manager class. Now we are going to create this manager class. One of its main purposes is to communicate with the SQLiteDatabase object. Our class will call methods of the database object, the database object will send queries to the database and return data to our manager class for output to our application. During the last section, we created the outline for our new class (AABDatabaseManager). We now need to include functionality in our class for retrieving, adding, and updating data to and from the database. I am going to make five different methods: addRow(), deleteRow(), updateRow(), getRow(), and getAllRows(). Lets go over these methods one at a time.

The addRow() Method

Our addRow() method of the database manager class is going to take two String arguments and ask the database object to add them as a new row in the given database table. The database object has an insert() method that we need to call. The insert() method takes three arguments.> The first argument is the table name. We will pass the constant TABLE_NAME here. The next argument requires a nullColumnHack type. This has to do with whether the row can be empty or not. We are going to pass in null as the variable here. In this instance, our database will not allow empty rows. The final argument for the databases insert() method requires the type ContentValues. A ContentValues object is a modified java collection that contains key/value pairs. We need to create a ContentValues object and pass it two key/value pairs to satisfy the database requirement (the id column will fill in automatically, that leaves the text_one and text_twocolumns). Once we have our ContentValues object, we can call the insert() method to add the new data to the database. I will always place database method calls inside of try/catch blocks to pass error messages to the log for debugging. Lets take a look at this code: public void addRow(String rowStringOne, String rowStringTwo) { // this is a key value pair holder used by android's SQLite functions ContentValues values = new ContentValues(); // this is how you add a value to a ContentValues object // we are passing in a key string and a value string each time values.put(TABLE_ROW_ONE, rowStringOne); values.put(TABLE_ROW_TWO, rowStringTwo); // ask the database object to insert the new data try { db.insert(TABLE_NAME, null, values); } catch(Exception e) { Log.e("DB ERROR", e.toString()); // prints the error message to the log e.printStackTrace(); // prints the stack trace to the log } }

<="" a="" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: rgb(64, 90, 106); font-weight: 700; text-decoration: none; ">The deleteRow() Method
<="" a="" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; paddingright: 0px; padding-bottom: 0px; padding-left: 0px; color: rgb(64, 90, 106); font-weight: 700; text-decoration: none; "> The deleteRow() method is a little simpler. It is going to take one argument, long rowID. We use a Long instead of an Integer because integers in SQLite are larger than integers in Java. Collecting data from a SQLite database and storing it in a Java Integer type may result in data loss. Using Long when dealing with SQLite in Java is recommended. Once we

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

pass in our rowID argument, we are going to call the SQLiteDatabase objects delete() method, passing it three arguments. The first argument, again, is the table name we are deleting a row from our TABLE_NAME constant. The second argument we pass is the SQLite WHERE clause without the word where. We need the id to equal the id we want to be deleted. Since this is a String argument, we can build the String inside the method call like this: TABLE_ROW_ID + = + rowID. Here is the deleteRow() method: public void deleteRow(long rowID) { // ask the database manager to delete the row of given id try { db.delete(TABLE_NAME, TABLE_ROW_ID + "=" + rowID, null); } catch (Exception e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } }

<="" a="" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: rgb(64, 90, 106); font-weight: 700; text-decoration: none; ">The updateRow() Method
The updateRow() method requires three arguments: the rowID, rowStringOne, and rowStringTwo. Like in the previousaddRow() method, we create our ContentValues object and give it two key/value pairs. Once everything is ready, we send it to the database object inside of a try/catch block. Here, the SQLite database object has an update() method that takes four arguments. The first argument is the table name. The next argument is the ContentValues object. The third argument is the SQLite WHERE clause. Again we put this string together inside of the method call. The final argument is the WHERE clause arugments. Typically, you could specify a String array of arguments to pass in for the WHERE conditions. For this case there is only one argument (that weve already specified) so we will pass in null. Here is the entire method: public void updateRow(long rowID, String rowStringOne, String rowStringTwo) { // this is a key value pair holder used by android's SQLite functions ContentValues values = new ContentValues(); values.put(TABLE_ROW_ONE, rowStringOne); values.put(TABLE_ROW_TWO, rowStringTwo); // ask the database object to update the database row of given rowID try {db.update(TABLE_NAME, values, TABLE_ROW_ID + "=" + rowID, null);} catch (Exception e) { Log.e("DB Error", e.toString()); e.printStackTrace(); } }

The getRowAsArray() and getAllRowsAsArrays() Methods


These next two methods are going to exemplify the use of Cursor objects. When we retrieve a single row or multiple rows from the SQLiteDatabase object, we recieve back a Cursor. These Cursor objects act as containers and iterators for the rows of data that are returned. I chose to have our methods convert the data inside the Cursor to ArrayLists. Its probably cleaner to just pass the Cursor out but I want to illistrate how to collect data from these objects. I will explain the getAllRowsAsArrays() method and not the getRowAsArray() method. Once you figure out the getAllRowsAsArrays()method, figuring out the getRowAsArray() should hopefully not be a problem. This is the most complex method so far. Before we get started, Id like to briefly cover the casting required for the ArrayList. Casting an ArrayList looks like this:ArrayList<ObjectType>. Inside of the brackets is where you specify what type of objects will be stored inside of the

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

ArrayList. In this example, we have ArrayLists of unspecified objects inside of an ArrayList. If we were to declare an ArrayList of Objects. We would write it: ArrayList<Object>(). Since the type in the ArrayList we are going to use is an ArrayList of Objects we place that inside of an ArrayList declaration. Our ArrrayList declaration looks like this: ArrayList<ArrayList<Object>> dataArrays = new ArrayList<ArrayList<Object>>(); Lets move on to the getRowsAsArrays() method. The first thing we do is create an ArrayList of ArrayLists of Objects to store the data in the method. I just covered how to do that. The next thing we need to do is create our Cursor object. I declare the Cursor object. Then, when I create it, I do so by calling the database objects query() method. Remember that the query() method returns a Cursor object. Our query() method takes seven arguments. Fortunately, we only need to worry about the first two to collect data from all rows in the datase. The first argument is the table name to gather data from. The second argument is an array of the names (Strings) of each column we want the query to return. If we want data from two columns, we need two column names. If we want data from five columns, we would need five column names. In our case, we are going to collect data from all three columns in our example table: the id, table_row_one, and table_row_two. We can pass the value null five times for the next five arguments. That gives us all seven arguments for the SQLiteDatabase objects query() method. Our getRowAsArray() method passes a third argument to the query() method. This argument, a String, satisifies the WHERE clause by specifying TABLE_ROW_ID=rowID. This is similar to the deleteRow() methods WHERE clause that I covered earlier in this section. So when we write the getRowAsArray() method. We pass in three arguments and and four null arugments to the SQLiteDatabases query() method. Lets go ahead and take a look at what we have so far in this method. In this code I have expanded the database query()method call into seven lines. I prefer to do this for readability you may or may not want to do the same. public ArrayList<Object> getRowAsArray(long rowID) { ArrayList<Object> rowArray = new ArrayList<Object>(); Cursor cursor; try { // this method call is spread out over seven lines as a personal preference cursor = db.query ( TABLE_NAME, new String[] { TABLE_ROW_ID, TABLE_ROW_ONE, TABLE_ROW_TWO }, TABLE_ROW_ID + "=" + rowID, null, null, null, null, null ); // TODO Move the data from the cursor to the arraylist. } catch (SQLException e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } return rowArray; } At this point we have a cursor object that has the data from all of the rows of the database table. The next thing we are going to do is collect this data from the cursor and store it in an ArrayList. Since the cursor object filled itself one item at a time, the cursors pointer is at the end of the cursor. You can think of this like a type writer typing out a row of text. After each letter is typed, the pointer (the little arrow showing you where the next letter is going to go) points at the next available space to type a new letter. Our cursor object inserted data into each space and now its pointer is at the end of the data that it entered. We want to read the data from the beginning so we need to move the pointer there. We do this with the Cursors moveToFirst() function. Once the pointer is at the front of the data, we can iterate through the cursor and collect the data as we go. Lets iterate through the cursor with a do/while block. We are going to do the data collecting while the

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Cursors moveToNext()method returns false. The moveToNext() method simply tries to move the pointer to the next row of data. If there are any available rows ahead, it goes to the next one and returns true. If there are no more rows, the method does nothing and returns false. do { // TODO: put data from current cursor row into an array // TODO: put the new array into the array of arrays } // try to move the cursor's pointer forward one position. while (cursor.moveToNext()); With our pointer now on the first row of data, we need to collect the data from that row from within the do block. The first thing I do is create a new ArrayList of Objects. In order to add the first piece of data I use one of the cursors get methods. There is a get method for each of the cursors supported data types. We know the data from the first column of data is the id of type Long. This means we need to use the cursors getLong() method. Each of these get methods takes a single argument that represents the column of data we want the data from. When we created the cursor object, the second arugment we passed to the query() method was an array of three Strings. The position of each of these column name Strings in the array of Strings corresponds to the needed argument in the Cursorsget functions. If we want the TABLE_ROW_ID (id) columns data, we look at the array of Strings, see that it is in position zero in that array, and decide that we need to pass 0 to getLong() to get back the id of the first row. All we have to do to get the id into the ArrayList is call the ArrayLists add() method, passing in cursor.getLong(0) as the argument. cursor.getLong(0) will pass the data into add() which will add the data to the ArrayList. After that we do the same for the next two positions of data using getString() twice passing in 1 and 2 consecutively. Here is the do while list that will collect the data from the ArrayList: do { ArrayList<Object> dataList = new ArrayList<Object>(); dataList.add(cursor.getLong(0)); dataList.add(cursor.getString(1)); dataList.add(cursor.getString(2)); dataArrays.add(dataList); } // move the cursor's pointer up one position. while (cursor.moveToNext()); The only other thing that we need to worry about here is the possibilty that there are no rows of data for the cursor to collect. This would leave us with an empty cursor. I went ahead and placed this do/while block inside of an if block so that it only runs if !cursor.isAfterLast() returns true. That is, once the cursors pointer was moved to the beginning of the cursor, if the pointer is not after the last row (ie., the beginning is not the end), then run the do/while code block. Once our do/while code completes, we can return our newly created ArrayList and we are done with writing this method. Whew! I apologize if this has been long winded. I am trying to make this as short and concise as I canso time to move on. Lets take a look at the two methods we just discussed. I went ahead and left some documentation in the code. /********************************************************************** * RETRIEVING ALL ROWS FROM THE DATABASE TABLE * * This is an example of how to retrieve all data from a database * table using this class. You should edit this method to suit your * needs. * * the key is automatically assigned by the database */ public ArrayList<ArrayList<Object>> getAllRowsAsArrays() { // create an ArrayList that will hold all of the data collected from // the database.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

ArrayList<ArrayList<Object>> dataArrays = new ArrayList<ArrayList<Object>>(); // this is a database call that creates a "cursor" object. // the cursor object store the information collected from the // database and is used to iterate through the data. Cursor cursor; try { // ask the database object to create the cursor. cursor = db.query( TABLE_NAME, new String[]{TABLE_ROW_ID, TABLE_ROW_ONE, TABLE_ROW_TWO}, null, null, null, null, null ); // move the cursor's pointer to position zero. cursor.moveToFirst(); // if there is data after the current cursor position, add it // to the ArrayList. if (!cursor.isAfterLast()) { do { ArrayList<Object> dataList = new ArrayList<Object>(); dataList.add(cursor.getLong(0)); dataList.add(cursor.getString(1)); dataList.add(cursor.getString(2)); dataArrays.add(dataList); } // move the cursor's pointer up one position. while (cursor.moveToNext()); } } catch (SQLException e) { Log.e("DB Error", e.toString()); e.printStackTrace(); } // return the ArrayList that holds the data collected from // the database. return dataArrays; }

/********************************************************************** * RETRIEVING A ROW FROM THE DATABASE TABLE * * This is an example of how to retrieve a row from a database table * using this class. You should edit this method to suit your needs. * * @param rowID the id of the row to retrieve * @return an array containing the data from the row */

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

public ArrayList<Object> getRowAsArray(long rowID) { // create an array list to store data from the database row. // I would recommend creating a JavaBean compliant object // to store this data instead. That way you can ensure // data types are correct. ArrayList<Object> rowArray = new ArrayList<Object>(); Cursor cursor; try { // this is a database call that creates a "cursor" object. // the cursor object store the information collected from the // database and is used to iterate through the data. cursor = db.query ( TABLE_NAME, new String[] { TABLE_ROW_ID, TABLE_ROW_ONE, TABLE_ROW_TWO }, TABLE_ROW_ID + "=" + rowID, null, null, null, null, null ); // move the pointer to position zero in the cursor. cursor.moveToFirst(); // if there is data available after the cursor's pointer, add // it to the ArrayList that will be returned by the method. if (!cursor.isAfterLast()) { do { rowArray.add(cursor.getLong(0)); rowArray.add(cursor.getString(1)); rowArray.add(cursor.getString(2)); } while (cursor.moveToNext()); } // let java know that you are through with the cursor. cursor.close(); } catch (SQLException e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } // return the ArrayList containing the given row from the database. return rowArray; } Believe it or not, we are now finished with our entire database manager object. It can be easily modified and patched into many small projects. The next page is the entire code. After that, we are going to write a small Activity that uses our new class. If you can handle that part then you are done! Thank you for reading. I hope it was helpful and please leave feedback if you have any. If you would like to see this database manager class in action, read on.

CODE: The AABDatabaseManager.java class

package com.anotherandroidblog.tools.database; import java.util.ArrayList;

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

import import import import import import import

android.content.ContentValues; android.content.Context; android.database.Cursor; android.database.SQLException; android.database.sqlite.SQLiteDatabase; android.database.sqlite.SQLiteOpenHelper; android.util.Log;

public class AABDatabaseManager { // the Activity or Application that is creating an object from this class. Context context; // a reference to the database used by this application/object private SQLiteDatabase db; // These constants are specific to the database. // changed to suit your needs. private final String DB_NAME = "database_name"; private final int DB_VERSION = 1; They should be

// These constants are specific to the database table. // changed to suit your needs. private final String TABLE_NAME = "database_table"; private final String TABLE_ROW_ID = "id"; private final String TABLE_ROW_ONE = "table_row_one"; private final String TABLE_ROW_TWO = "table_row_two"; public AABDatabaseManager(Context context) { this.context = context;

They should be

// create or open the database CustomSQLiteOpenHelper helper = new CustomSQLiteOpenHelper(context); this.db = helper.getWritableDatabase(); }

/********************************************************************** * ADDING A ROW TO THE DATABASE TABLE * * This is an example of how to add a row to a database table * using this class. You should edit this method to suit your * needs. * * the key is automatically assigned by the database * @param rowStringOne the value for the row's first column * @param rowStringTwo the value for the row's second column */ public void addRow(String rowStringOne, String rowStringTwo) { // this is a key value pair holder used by android's SQLite functions ContentValues values = new ContentValues(); values.put(TABLE_ROW_ONE, rowStringOne); values.put(TABLE_ROW_TWO, rowStringTwo); // ask the database object to insert the new data try{db.insert(TABLE_NAME, null, values);}

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

catch(Exception e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } }

/********************************************************************** * DELETING A ROW FROM THE DATABASE TABLE * * This is an example of how to delete a row from a database table * using this class. In most cases, this method probably does * not need to be rewritten. * * @param rowID the SQLite database identifier for the row to delete. */ public void deleteRow(long rowID) { // ask the database manager to delete the row of given id try {db.delete(TABLE_NAME, TABLE_ROW_ID + "=" + rowID, null);} catch (Exception e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } } /********************************************************************** * UPDATING A ROW IN THE DATABASE TABLE * * This is an example of how to update a row in the database table * using this class. You should edit this method to suit your needs. * * @param rowID the SQLite database identifier for the row to update. * @param rowStringOne the new value for the row's first column * @param rowStringTwo the new value for the row's second column */ public void updateRow(long rowID, String rowStringOne, String rowStringTwo) { // this is a key value pair holder used by android's SQLite functions ContentValues values = new ContentValues(); values.put(TABLE_ROW_ONE, rowStringOne); values.put(TABLE_ROW_TWO, rowStringTwo); // ask the database object to update the database row of given rowID try {db.update(TABLE_NAME, values, TABLE_ROW_ID + "=" + rowID, null);} catch (Exception e) { Log.e("DB Error", e.toString()); e.printStackTrace(); } } /********************************************************************** * RETRIEVING A ROW FROM THE DATABASE TABLE * * This is an example of how to retrieve a row from a database table * using this class. You should edit this method to suit your needs. *

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

* @param rowID the id of the row to retrieve * @return an array containing the data from the row */ public ArrayList<Object> getRowAsArray(long rowID) { // create an array list to store data from the database row. // I would recommend creating a JavaBean compliant object // to store this data instead. That way you can ensure // data types are correct. ArrayList<Object> rowArray = new ArrayList<Object>(); Cursor cursor; try { // this is a database call that creates a "cursor" object. // the cursor object store the information collected from the // database and is used to iterate through the data. cursor = db.query ( TABLE_NAME, new String[] { TABLE_ROW_ID, TABLE_ROW_ONE, TABLE_ROW_TWO }, TABLE_ROW_ID + "=" + rowID, null, null, null, null, null ); // move the pointer to position zero in the cursor. cursor.moveToFirst(); // if there is data available after the cursor's pointer, add // it to the ArrayList that will be returned by the method. if (!cursor.isAfterLast()) { do { rowArray.add(cursor.getLong(0)); rowArray.add(cursor.getString(1)); rowArray.add(cursor.getString(2)); } while (cursor.moveToNext()); } // let java know that you are through with the cursor. cursor.close(); } catch (SQLException e) { Log.e("DB ERROR", e.toString()); e.printStackTrace(); } // return the ArrayList containing the given row from the database. return rowArray; }

/********************************************************************** * RETRIEVING ALL ROWS FROM THE DATABASE TABLE *

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

* This is an example of how to retrieve all data from a database * table using this class. You should edit this method to suit your * needs. * * the key is automatically assigned by the database */ public ArrayList<ArrayList<Object>> getAllRowsAsArrays() { // create an ArrayList that will hold all of the data collected from // the database. ArrayList<ArrayList<Object>> dataArrays = new ArrayList<ArrayList<Object>>(); // this is a database call that creates a "cursor" object. // the cursor object store the information collected from the // database and is used to iterate through the data. Cursor cursor; try { // ask the database object to create the cursor. cursor = db.query( TABLE_NAME, new String[]{TABLE_ROW_ID, TABLE_ROW_ONE, TABLE_ROW_TWO}, null, null, null, null, null ); // move the cursor's pointer to position zero. cursor.moveToFirst(); // if there is data after the current cursor position, add it // to the ArrayList. if (!cursor.isAfterLast()) { do { ArrayList<Object> dataList = new ArrayList<Object>(); dataList.add(cursor.getLong(0)); dataList.add(cursor.getString(1)); dataList.add(cursor.getString(2)); dataArrays.add(dataList); } // move the cursor's pointer up one position. while (cursor.moveToNext()); } } catch (SQLException e) { Log.e("DB Error", e.toString()); e.printStackTrace(); } // return the ArrayList that holds the data collected from // the database. return dataArrays; }

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

/********************************************************************** * THIS IS THE BEGINNING OF THE INTERNAL SQLiteOpenHelper SUBCLASS. * * I MADE THIS CLASS INTERNAL SO I CAN COPY A SINGLE FILE TO NEW APPS * AND MODIFYING IT - ACHIEVING DATABASE FUNCTIONALITY. ALSO, THIS WAY * I DO NOT HAVE TO SHARE CONSTANTS BETWEEN TWO FILES AND CAN * INSTEAD MAKE THEM PRIVATE AND/OR NON-STATIC. HOWEVER, I THINK THE * INDUSTRY STANDARD IS TO KEEP THIS CLASS IN A SEPARATE FILE. *********************************************************************/ /** * This class is designed to check if there is a database that currently * exists for the given program. If the database does not exist, it creates * one. After the class ensures that the database exists, this class * will open the database for use. Most of this functionality will be * handled by the SQLiteOpenHelper parent class. The purpose of extending * this class is to tell the class how to create (or update) the database. * * @author Randall Mitchell * */ private class CustomSQLiteOpenHelper extends SQLiteOpenHelper { public CustomSQLiteOpenHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { // This string is used to create the database. // be changed to suit your needs. String newTableQueryString = "create table " +

It should TABLE_NAME + " (" + TABLE_ROW_ID

+ " integer primary key autoincrement not null," + TABLE_ROW_ONE + " text," + TABLE_ROW_TWO + " text" + ");"; // execute the query string to the database. db.execSQL(newTableQueryString); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // NOTHING TO DO HERE. THIS IS THE ORIGINAL DATABASE VERSION. // OTHERWISE, YOU WOULD SPECIFIY HOW TO UPGRADE THE DATABASE. } } }

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

The Example Application Explained


Lets briefly discuss how the Application works. Ive created the entire application within a single Activity. In the database there are three columns: id (integer), table_row_one (string), and table_row_two (string). Our activity is going to have three forms for the user to adjust data within the database. The three forms handle adding, deleting, and editing rows. There will also be instruction that tell the user on how to use each of the forms. Below the three forms is a table that displays the database table data to the user. The table consists of one column for each database table column and one row for each database row. Lets take a look at each of the forms.

Form for Adding Data

The first form is for adding data to the database. The form will have two form fields where the user can enter values. The form fields correspond to table_row_one and table_row_two in the database. After the two form fields, there is a submit button for the user to submit entered data. The applications database object will automatically assign an id to the new data.

Form For Deleting Data

The second form is used to delete a row from the database. It will have a single form field where the user will enter a value that corresponds to an id in the database table. Next to the id field is a delete button for the user to submit the form.

Form For Retrieving and Editing Data

The final form will allow the user to change a row of data. This form will have two buttons and three form fields. The first form field is for the user to enter an id. After this form field is a get button that the user can use to retrieve data into the next two form fields. Then the user can change the data in these fields and hit the update button to update the data.

The Application XML


I am not going to go over the XML layout in detail. In short, I have layed out out each form in a row, layed out the forms and instructions from top to bottom, and given each of the form widgets an id so that they can be referenced in Java, and referenced any text to a String in the Strings xml resource file. If you have any questions about the XML feel free to leave a comment to the posting with your question(s). Here is the complete layout XML file (main.xml): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <!-- ADD A DATA ENTRY FORM --> <TextView android:text="@string/add_directions" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/text_field_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="100px" /> <EditText android:id="@+id/text_field_two"

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="100px" /> <Button android:id="@+id/add_button" android:text="@string/add" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <!-- DELETE A DATA ENTRY FORM --> <TextView android:text="@string/delete_directions" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/id_field" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="100px" /> <Button android:id="@+id/delete_button" android:text="@string/delete" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <!-- UPDATE A DATA ENTRY FORM --> <TextView android:text="@string/update_directions" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/update_id_field" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="45px" /> <Button android:id="@+id/retrieve_button" android:text="@string/retrieve" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText android:id="@+id/update_text_field_one"

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="70px" /> <EditText android:id="@+id/update_text_field_two" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="70px" /> <Button android:id="@+id/update_button" android:text="@string/update" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <!-- THE DATA TABLE --> <TableLayout android:id="@+id/data_table" android:layout_width="fill_parent" android:layout_height="wrap_content" > <TableRow> <TextView android:text="@string/th_id" android:minWidth="50px" /> <TextView android:text="@string/th_text_one" android:minWidth="125px" /> <TextView android:text="@string/th_text_two" android:minWidth="125px" /> </TableRow> </TableLayout> </LinearLayout> And here is the strings.xml resource file: <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Database Application</string> <string name="add_directions">Fill in both fields with text and click the "add" button.</string> <string name="add">add</string> <string name="delete_directions">To delete a row, type the "id" of the row in the field provided and press the "delete" button.</string> <string name="delete">delete</string> <string first field <string <string name="update_directions">To update a row, type the "id" of the row in the and type the new information into the next two fields.</string> name="retrieve">get</string> name="update">update</string>

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

<string name="th_id">ID</string> <string name="th_text_one">Text Field One</string> <string name="th_text_two">Text Field Two</string> </resources> With the XML now in place, lets move on to the Activity class.

The Main Activity


The first thing we need to do is establish some object variables. There should be a variable for each of the form fields buttons. We will also need declarations for our table object and our fancy new database manager object. Lets take a look at the beginning of our activity class: package com.anotherandroidblog.tools.database; import java.util.ArrayList; import import import import import import import import android.app.Activity; android.os.Bundle; android.util.Log; android.widget.Button; android.widget.EditText; android.widget.TableLayout; android.widget.TableRow; android.widget.TextView;

public class DatabaseExampleActivity extends Activity { // the text fields that users input new data into EditText textFieldOne, textFieldTwo, idField, updateIDField, updateTextFieldOne, updateTextFieldTwo; // the buttons that listen for the user to select an action Button addButton, deleteButton, retrieveButton, updateButton; // the table that displays the data TableLayout dataTable; // the class that opens or creates the database and makes sql calls to it AABDatabaseManager db; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { // this try catch block returns better error reporting to the log try { // Android OS specific calls super.onCreate(savedInstanceState); setContentView(R.layout.main); // TODO complete the setup operations } catch (Exception e) { Log.e("ERROR", e.toString()); e.printStackTrace(); } // TODO create the rest of the methods

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

} Just as a note, the try/catch block is a method I use for improved error reporting in Android. Check out this post for further explaination.

Connecting the Layout to Java

In our Java code, we need to connect our variables to the fields, buttons, and table that were all created in our XML file. I am going to make a method that specifically handles this task: setupViews(). The method used in Android to connect a variable to an XML object is to use the findViewByID() method. This method takes one argument the resource id of the view widget. The important thing to know about findViewByID() is that we need to cast it to a specific widget type. We do this by placing the type in parenthesis in front of the method call. We will use the assignment(=) operator to assign each variable to the type cast object returned by findViewByID. There needs to be one statement for each of the view widgets we wish to work with in Java. Lets take a look at the method code: /** * creates references and listeners for the GUI interface */ private void setupViews() { // THE DATA TABLE dataTable= (TableLayout)findViewById(R.id.data_table); // THE DATA FORM FIELDS textFieldOne= (EditText)findViewById(R.id.text_field_one); textFieldTwo= (EditText)findViewById(R.id.text_field_two); idField= (EditText)findViewById(R.id.id_field); updateIDField= (EditText)findViewById(R.id.update_id_field); updateTextFieldOne= (EditText)findViewById(R.id.update_text_field_one); updateTextFieldTwo= (EditText)findViewById(R.id.update_text_field_two); // THE BUTTONS addButton = deleteButton = retrieveButton = updateButton = (Button)findViewById(R.id.add_button); (Button)findViewById(R.id.delete_button); (Button)findViewById(R.id.retrieve_button); (Button)findViewById(R.id.update_button);

} This and several other methods will be called from within onCreate() as a means of setting up our Activity.

Enabling the Buttons


Now that we have all of our view widgets assigned to variables, we need to make our buttons work. I am going to create one method for enabling the buttons and then a method for each of the buttons: addRow(), deleteRow(), retrieveRow(), and updateRow(). These methods will be called when a particular button is pushed. Here is the addButtonListeners() method that ties each button to the correct method: /** * adds listeners to each of the buttons and sets them to call relevant methods */ private void addButtonListeners() { addButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {addRow();} } ); deleteButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {deleteRow();}

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

} ); updateButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {updateRow();} } ); retrieveButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {retrieveRow();} } ); } A few note worthy points. We attach OnClickListener objects to a button by using the buttons setOnClickListener()method. It is common to create the OnClickListener objects inside of the method call as seen here. When we create these generic listeners, we need to override their onClick() method with our desired code. The desired code will simply be a call to the appropraite method.

<="" a="" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: rgb(64, 90, 106); font-weight: 700; text-decoration: none; ">Communicating with the Database Manager Object
<="" a="" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; color: rgb(51, 51, 51); font-weight: 700; text-decoration: underline; ">
Lets recap first. We have references to all of our view widgets ready to use inside of Java. We have each of the buttons in our application tied to OnClickListeners. When the user clicks a button, the listener will call a specific method. We need to create those methods. When I create the methods, I am going to put their internal code inside of a try/catch statement. I do this because we are dealing with user input. There is always a chance of the user inputting bad data. For the purpose of this tutorial, I am not going to create a system of communicating errors with the end user. Instead, I will simply feed any errors to the Java console/log. Why dont we go through each of our methods in turn.

The addRow() Method

Our addRow() method collects the input from the first forms fields. The is handled by calling the getText()method of the TextView form fields combined with the toString() method of the getText() return type. It looks like this: textFieldOne.getText().toString(). It uses this data as arguments in a call to the database managers addRow() method. After the data is sent to the database, addRow() /** * adds a row to the database based on information contained in the * add row form fields. */ private void addRow() { try { // ask the database manager to add a row given the two strings // this is addRow() in the activity calling addRow() in the database object

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

db.addRow ( textFieldOne.getText().toString(), textFieldTwo.getText().toString() ); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) { Log.e("Add Error", e.toString()); e.printStackTrace(); } }

The deleteRow() Method


The deleteRow() method is responsible for deleting a row from the database. The database manager that we created earlier has a corresponding deleteRow() method. We collect and pass the value that the user enters into the idField with idField.getText().toString(). Once the data row is deleted, we update our table widget and empty our form fields. Here is our method code: /** * deletes a row from the database with the id number in the corresponding * user entry field */ private void deleteRow() { try { // ask the database manager to delete the row with the give rowID. db.deleteRow(Long.parseLong(idField.getText().toString())); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) { Log.e("Delete Error", e.toString()); e.printStackTrace(); } }

The retrieveRow() Method


When the user fills in the id field of the last form with a valid id and presses the get button,retrieveRow() is called. Lets go over the code inside of retrieveRow(). First we declare a local ArrayList object and instantiates it by calling the datatabase manangers getRowAsArray() method. ThegetRowAsArray() method in the database manager takes a Long as its only argument the row id. We collect the value from the id form field as a String. order to covert the String to a Long, we use the Long class static method parseLong(String). The Long can now be passed to getRowAsArray(). Here is that code: ArrayList<Object> row; row = db.getRowAsArray( Long.parseLong( updateIDField.getText().toString() ) ); Once we have our ArrayList populated with data, we can pass that data to the two remaining form fields. We do this using the setText() method of the EditText widgets.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Because of the way we set up the database manager, Java doesnt know what types of values are in the ArrayList. This means we need to cast the ArrayList items of unkown type to String using a(String) cast. Here is the code for our retrieveRow() method. /** * retrieves a row from the database with the id number in the corresponding * user entry field */ private void retrieveRow() { try { // The ArrayList that holds the row data ArrayList<Object> row; // ask the database manager to retrieve the row with the given rowID row = db.getRowAsArray(Long.parseLong(updateIDField.getText().toString())); // update the form fields to hold the retrieved data updateTextFieldOne.setText((String)row.get(1)); updateTextFieldTwo.setText((String)row.get(2)); } catch (Exception e) { Log.e("Retrieve Error", e.toString()); e.printStackTrace(); } }

The updateRow() Method


Our Activitys updateRow() method calls the database manangers updateRow() method, passing it three arguments. The first value passed is a row id. Here again, we use Long.parseLong(String), passing it the String from the updateIDField() method. The next two arguments we pass updateRow() are String values that we retrieve from updateTextFieldOne() and updateTextFieldTwo(). The database manager updates the given row and we can update our table and empty our form fields. Here is updateRow() /** * updates a row with the given information in the corresponding user entry * fields */ private void updateRow() { try { // ask the database manager to update the row based on the information // found in the corresponding user entry fields db.updateRow ( Long.parseLong(updateIDField.getText().toString()), updateTextFieldOne.getText().toString(), updateTextFieldTwo.getText().toString() ); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) {

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Log.e("Update Error", e.toString()); e.printStackTrace(); } }

The updateTable() Method


The table I set up has column headers inside of the first row of our table. When updateTable() is called, it deletes all the table rows except the top row (of headers), and then recreates the rows based upon the current state of the database. Deleting the rows is handled by a while block. We can determine how many rows are in the table using itsgetChildCount() method. The while block logic reads while the child count is greater than one, delete the row in the second position. This will delete all rows except the first row. Dont forget that the count starts at zero, so the second position is 1. This is our while block. while (dataTable.getChildCount() > 1) { dataTable.removeViewAt(1); } Now, we have an empty table ready for use. We need to get that data ready to enter into the table. First, I declare an ArrayList of ArrayLists of Objects and instantiate it making it the return value of the database managers getAllRowsAsArray() method as follows. ArrayList<ArrayList<Object>> data = db.getAllRowsAsArrays(); Then we can iterate through data and create table rows as we go. I set up an iteration block that will create a row, fill it with data, and add it to our table with each iteration of the ArrayList. Inside of our block, I create and instantiate a new TableRow object: TableRow tableRow = new TableRow(this);. I declare an ArrayList of Objects and tie it to the row that the iterator is currently iterating. For each for item, I need to create a separate TextView object and add it to the new TableRow. Finally, I add the TableRow to the data table using the tables addView() method. Here is the updateTable() method: /** * updates the table from the database. */ private void updateTable() { // delete all but the first row. remember that the count // starts at one and the index starts at zero while (dataTable.getChildCount() > 1) { // while there are at least two rows in the table widget, delete // the second row. dataTable.removeViewAt(1); } // collect all row data from the database and // store it in a two dimensional ArrayList ArrayList<ArrayList<Object>> data = db.getAllRowsAsArrays(); // iterate the ArrayList, create new rows each time and add them // to the table widget. for (int position=0; position < data.size(); position++) { TableRow tableRow= new TableRow(this); ArrayList<Object> row = data.get(position); TextView idText = new TextView(this); idText.setText(row.get(0).toString()); tableRow.addView(idText); TextView textOne = new TextView(this); textOne.setText(row.get(1).toString()); tableRow.addView(textOne);

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

TextView textTwo = new TextView(this); textTwo.setText(row.get(2).toString()); tableRow.addView(textTwo); dataTable.addView(tableRow); } } Thats it for updating the table widget. The only thing left to cover is the little helper class for clearing the form data. All it does is call each of the applications form fields setText() methods, passing in an empty String (). Here is the code for that: /** * helper method to empty all the fields in all the forms. */ private void emptyFormFields() { textFieldOne.setText(""); textFieldTwo.setText(""); idField.setText(""); updateIDField.setText(""); updateTextFieldOne.setText(""); updateTextFieldTwo.setText(""); } The entire code for the Activity is next

The DatabaseExampleActivity.java Class


package com.anotherandroidblog.tools.database; import java.util.ArrayList; import import import import import import import import import android.app.Activity; android.os.Bundle; android.util.Log; android.view.View; android.widget.Button; android.widget.EditText; android.widget.TableLayout; android.widget.TableRow; android.widget.TextView;

public class DatabaseExampleActivity extends Activity { // the text fields that users input new data into EditText textFieldOne, textFieldTwo, idField, updateIDField, updateTextFieldOne, updateTextFieldTwo; // the buttons that listen for the user to select an action Button addButton, deleteButton, retrieveButton, updateButton; // the table that displays the data TableLayout dataTable; // the class that opens or creates the database and makes sql calls to it AABDatabaseManager db; /** Called when the activity is first created. */ @Override

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

public void onCreate(Bundle savedInstanceState) { // this try catch block returns better error reporting to the log try { // Android specific calls super.onCreate(savedInstanceState); setContentView(R.layout.main); // create the database manager object db = new AABDatabaseManager(this); // create references and listeners for the GUI interface setupViews(); // make the buttons clicks perform actions addButtonListeners(); // load the data table updateTable(); } catch (Exception e) { Log.e("ERROR", e.toString()); e.printStackTrace(); } }

/** * creates references and listeners for the GUI interface */ private void setupViews() { // THE DATA TABLE dataTable= (TableLayout)findViewById(R.id.data_table); // THE DATA FORM FIELDS textFieldOne= (EditText)findViewById(R.id.text_field_one); textFieldTwo= (EditText)findViewById(R.id.text_field_two); idField= (EditText)findViewById(R.id.id_field); updateIDField= (EditText)findViewById(R.id.update_id_field); updateTextFieldOne= (EditText)findViewById(R.id.update_text_field_one); updateTextFieldTwo= (EditText)findViewById(R.id.update_text_field_two); // THE BUTTONS addButton = deleteButton = retrieveButton = updateButton = } (Button)findViewById(R.id.add_button); (Button)findViewById(R.id.delete_button); (Button)findViewById(R.id.retrieve_button); (Button)findViewById(R.id.update_button);

/** * adds listeners to each of the buttons and sets them to call relevant methods */ private void addButtonListeners()

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

{ addButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {addRow();} } ); deleteButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {deleteRow();} } ); updateButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {updateRow();} } ); retrieveButton.setOnClickListener ( new View.OnClickListener() { @Override public void onClick(View v) {retrieveRow();} } ); }

/** * adds a row to the database based on information contained in the * add row fields. */ private void addRow() { try { // ask the database manager to add a row given the two strings db.addRow ( textFieldOne.getText().toString(), textFieldTwo.getText().toString() ); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) {

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Log.e("Add Error", e.toString()); e.printStackTrace(); } }

/** * deletes a row from the database with the id number in the corresponding * user entry field */ private void deleteRow() { try { // ask the database manager to delete the row with the give rowID. db.deleteRow(Long.parseLong(idField.getText().toString())); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) { Log.e("Delete Error", e.toString()); e.printStackTrace(); } }

/** * retrieves a row from the database with the id number in the corresponding * user entry field */ private void retrieveRow() { try { // The ArrayList that holds the row data ArrayList<Object> row; // ask the database manager to retrieve the row with the given rowID row = db.getRowAsArray(Long.parseLong(updateIDField.getText().toString())); // update the form fields to hold the retrieved data updateTextFieldOne.setText((String)row.get(1)); updateTextFieldTwo.setText((String)row.get(2)); } catch (Exception e) { Log.e("Retrieve Error", e.toString()); e.printStackTrace(); } }

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

/** * updates a row with the given information in the corresponding user entry * fields */ private void updateRow() { try { // ask the database manager to update the row based on the information // found in the corresponding user entry fields db.updateRow ( Long.parseLong(updateIDField.getText().toString()), updateTextFieldOne.getText().toString(), updateTextFieldTwo.getText().toString() ); // request the table be updated updateTable(); // remove all user input from the Activity emptyFormFields(); } catch (Exception e) { Log.e("Update Error", e.toString()); e.printStackTrace(); } }

/** * helper method to empty all the fields in all the forms. */ private void emptyFormFields() { textFieldOne.setText(""); textFieldTwo.setText(""); idField.setText(""); updateIDField.setText(""); updateTextFieldOne.setText(""); updateTextFieldTwo.setText(""); }

/** * updates the table from the database. */ private void updateTable() { // delete all but the first row. remember that the count // starts at one and the index starts at zero while (dataTable.getChildCount() > 1) { // while there are at least two rows in the table widget, delete // the second row.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

dataTable.removeViewAt(1); } // collect the current row information from the database and // store it in a two dimensional ArrayList ArrayList<ArrayList<Object>> data = db.getAllRowsAsArrays(); // iterate the ArrayList, create new rows each time and add them // to the table widget. for (int position=0; position < data.size(); position++) { TableRow tableRow= new TableRow(this); ArrayList<Object> row = data.get(position); TextView idText = new TextView(this); idText.setText(row.get(0).toString()); tableRow.addView(idText); TextView textOne = new TextView(this); textOne.setText(row.get(1).toString()); tableRow.addView(textOne); TextView textTwo = new TextView(this); textTwo.setText(row.get(2).toString()); tableRow.addView(textTwo); dataTable.addView(tableRow); } } }

Wrap Up
Once we completed the database manager class, creating and accessing a database became fairly a mundane task. Ive stored and created this class in such a way that it should be easy to modify and impelement into most applications. Not having to struggle with programming database communications throughout the development cycle will allow us to focus on the finer aspects of our applications. Please post to this blog with any questions, comments, or concerns. I would love to have them. In the mean time, keep plugging away at that keyboard; and remember, we are the future of the electronic world. Heres a direct link to the code download:

Android SQLite Basics: creating and using a database, and working with sqlite3
Submitted by charlie.collins on Sun, 01/17/2010 - 14:48

Tagged:

Tutorials

UPDATE DISCLAIMERThis is one way to do direct DB access with SQLite on Android, but it's not the only way. I have modified the pattern I personally use somewhat, and it's quite a bit different from the time this article was written (this article is old). I plan to update this article at some point when I have time, but for now just keep in mind that this is meant to get you started, but not be a complete/comprehensive/exclusive approach.

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

Because I often have to revisit this stuff myself, I thought I would write a quick reference tutorial on creating and using a database with an Android application. This isn't terribly well covered in the Android docs, and though many ContentProvider tutorials exist (such as the Unlocking Android code for chapter 5, and the NotePad tutorial included with the SDK), and these help a lot with general database concepts, they are really more complicated than what a basic application needs - a database to store and retrieve stuff. I will walk through the code and tools for an oversimplified example here, with the ultimate goal of inserting and retrieving some data from an database in an Android app, and then examining the database using a shell and the sqlite3 command line tool. The entire code for this example is available here: http://totsp.com/svn/repo/AndroidExamples/trunk/. NOTE This code was updated for part 2, and part 3 of this series, so it no longer exactly matches this example -- it's still a working sample app, it just now does a bit more than the original. First, to get this rolling, we need to create an Android application that HAS a database. We could use any built in application that has a database just to explore it, such as com.android.alarmclock), but we are going to create one here for completeness. After it's setup, the interface for our application will look like the screen shot shown below:

Yeah this project is ugly, and it only has one Activity, but for our purposes here we really aren't trying to create a fancy UI (or anything that is complicated on any level). To create this project we will use the Eclipse IDE. Along with Eclipse, we will also of course need to have the Android SDK with the correct Eclipse ADT Plug-In as a prerequisite too (to get that see the instructions at the aforelinked site). 1. To create a basic Android project, we will simply select File->New->Other->Android->Android Project. On the dialog we will then enter the application name AndroidExamples, and the package name com.totsp.androidexamples, along with some other settings, as shown in the figure below:

The target we choose must be one that we have installed when we setup or configued the Android SDK. For this example we are using 1.6 because it is still the most common Android platform that user's phones are running (we are using the 2.1 SDK, with a 1.6 Target). We will also set the "Min SDK Version" to "4" which is SDK 1.6. This is a confusing part of the Android setup at this point (names are transposed from "sdk" to "api" and such) but the details are documented somewhat. Once we have the default sample project in place, the next step will be to create a helper class that can create the database and encapsulate other SQL details. We will call this class DataHelper. Within this class (at the end) we will include an important inner class that provides a SQLiteOpenHelper. The full code is shown below: 01 package com.totsp.androidexamples; 02

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

03 im<wbr>port android.content.Context; 04 impor<wbr>t android.database.Cursor; 05 impor<wbr>t android.database.sqlite.SQLite<wbr>Database; 06 import android.database.sqlite.SQLite<wbr>OpenHelper; 07 import android.database.sqlite.SQLite<wbr>Statement; 08 import android.util.Log; 09 10 import java.util.ArrayList; 11 import java.util.List; 12 13 public class DataHelper { 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public List<String> selectAll() { List<String> list = new ArrayList<String>(); public void deleteAll() { this.db.delete(TABLE_NAME, null, null); } } public long insert(String name) { this.insertStmt.bindString(1, name); return this.insertStmt.executeInsert(<wbr>); } public DataHelper(Context context) { this.context = context; OpenHelper openHelper = new OpenHelper(this.context); this.db = openHelper.getWritableDatabase<wbr>(); this.insertStmt = this.db.compileStatement(INSER<wbr>T); private SQLiteStatement insertStmt; private static final String INSERT = "insert into " + TABLE_NAME + "(name) values (?)"; private Context context; private SQLiteDatabase db; private static final String DATABASE_NAME = "example.db"; private static final int DATABASE_VERSION = 1; private static final String TABLE_NAME = "table1";

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 } } }

Cursor cursor = this.db.query(TABLE_NAME, new String[] { "name" }, null, null, null, null, "name desc"); if (cursor.moveToFirst()) { do { list.add(cursor.getString(0));<wbr> } while (cursor.moveToNext()); } if (cursor != null && !cursor.isClosed()) { cursor.close(); } return list;

private static class OpenHelper extends SQLiteOpenHelper { OpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY, name TEXT)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w("Example", "Upgrading database, this will drop tables and recreate."); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); }

This class is simple, as I have said it would be. For example it's using a database with one table and one column, but, it still covers some core Android concepts. We won't go into great detail concerning this class here, it should be mostly understandable from the code, but a few important things to note are: it includes an implementation of SQLiteOpenHelper as an inner class it demonstrates two different ways of interacting with the database in code, with aSQLiteStatement for inserts (which has the advantage of being pre-compiled, versus regular, but easier SQLiteDatabase.query() methods you probably also want to be familiar with), and directly by querying for selects it shows a useful pattern (though again, oversimplified here) of exposing data persistence/retrieval methods on the helper

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

To use this class from the default Main.java class that we let the Android Eclipse Plug-In generate, we will modify it a bit to create an instance of our DataHelper and then use it to create and retrieve data as seen below.(NOTE: In the real world you might do this once per application, say by using the oft overlooked Android Application class, where you could create the DataHelper once, and then expose the reference to other classes, rather than in Activity.onCreate()): 01 package com.totsp.androidexamples; 02 03 im<wbr>port android.app.Activity; 04 import android.os.Bundle; 05 import android.util.Log; 06 import android.widget.TextView; 07 08 impo<wbr>rt java.util.List; 09 10 public class Main extends Activity { 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 this.output.setText(sb.toStrin<wbr>g()); Log.d("EXAMPLE", "names size - " + names.size()); this.dh = new DataHelper(this); this.dh.deleteAll(); this.dh.insert("Porky Pig"); this.dh.insert("Foghorn Leghorn"); this.dh.insert("Yosemite Sam"); List<String> names = this.dh.selectAll(); StringBuilder sb = new StringBuilder(); sb.append("Names in database:\n"); for (String name : names) { sb.append(name + "\n"); } this.output = (TextView) this.findViewById(R.id.out_tex<wbr>t); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceSt<wbr>ate); setContentView(R.layout.main);<wbr> private DataHelper dh; private TextView output;

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

38 39 40 } }

For this class to work, we also need to change the main.xml layout file it relies on. We need to include one additional TextView for the output, as seen below: 01 <?xml version="1.0" encoding="utf-8"?> 02 <Scro<wbr>llView 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 xmlns:android="http://schemas.<wbr>android.com/apk/res/android" android:layout_width="fill_par<wbr>ent" android:layout_height="wrap_co<wbr>ntent"> <LinearLayout xmlns:android= "http://schemas.android.com/ap<wbr>k/res/android" android:orientation="vertical"<wbr> android:layout_width="fill_par<wbr>ent" android:layout_height="fill_pa<wbr>rent"> <TextView android:layout_width="fill_par<wbr>ent" android:layout_height="wrap_co<wbr>ntent" android:text="@string/hello" /> <TextView android:id="@+id/out_text" android:layout_width="fill_par<wbr>ent" android:layout_height="wrap_co<wbr>ntent" android:text="" /> </LinearLayout>

19 </Scr<wbr>ollView> With that, we have an application that we should be able to launch and run in the emulator, and it should look like the first screen shot we saw above - a basic black screen with white text and a few names as output. The names come from the database, which now exists, and now we can move on to using sqlite3. Android uses SQLite as it's built in embedded database. If you need to store local application data, rather than going to simpler mechanisms like the file system, or more complicated means such as the network, you use the database. To examine the database we can login using the shell provided by the Android Debug Bridge. To use this we need the "tools" folder of the SDK on our path (see the SDK documentation if you need more information about that). If we start the app in Eclipse (Run As -> Android Application), and leave it running, we should then be able to login with the command: ccollins@crotalus:~$ adb -e shell #

The -e option tells ADB to use the emulator, rather than a possible connected device (and returns an error if more than one emulator is running). The "#" is the command prompt, we are logged in. Once logged in we can browse around with the "ls" command. The directory we are interested in is/data/data/com.totsp.androidexamples/databases (each application has a directory at the path /data/data/PACKAGE_NAME). We can change to that directory with the "cd" command. Once there we can use the command line SQLite tool, sqlite3, to examine our database, as follows: # sqlite3 example.db

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

SQLite version 3.5.9 Enter ".help" for instructions sqlite> select * from sqlite_master; table|android_metadata|android_metadata|3|CREATE TABLE android_metadata (locale TEXT) table|table1|table1|4|CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT) sqlite> We login with "sqlite3 [database_name]" and then we can run basic SQL commands that are supported by SQLite (see the documentation there for full details). One handy table is thesqlite_master that we can see shows all the other tables inside our database. Some other interesting commands are: sqlite> .schema CREATE TABLE android_metadata (locale TEXT); CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT); sqlite> .tables android_metadata table1 sqlite> select * from table1; 1|Porky Pig 2|Foghorn Leghorn 3|Yosemite Sam sqlite> We can use .help from within the sqlite3 shell (it has it's own shell, apart from the emulator shell we are logged in to), to see a full list of commands. Commands that start with "." are built in, and perform a function - such as .schema and .tables that we see above. SQL commands themselves can also be run directly by typing them in -- something we also see above (typed commands are run as soon as a ; is encountered and you press enter). With that, we have the basics. We have an application that creates a database and stores and retrieves data using it, and we have done a bit of exploring with the SQLite tools in ADB. In the future I hope to expand on this article and add some more involved tables, and further examples such as Android unit testing (another not so well documented, but extremely useful, part of the platform).

A7, Stephanos Tower, Eachamukku, Kakkanadu,Kochi

You might also like