You are on page 1of 12

232

Chapter 9 Data Storage Methods

Then, any application can automatically have the EULA functionality by simply putting
the following line in the onCreate() method of the main activity of the application:
Eula.show(this);

SQLite Database
For more complex data structures, a database provides a quicker and more flexible access
method than flat files or shared preferences.Android provides a built-in database called
SQLite that provides full relational database capability utilizing SQL commands. Each
application that uses SQLite has its own instance of the database, which is by default
accessible only from the application itself.The database is stored in the /data/data/
<package_name>/databases folder of an Android device.A Content Provider can be
used to share the database information between applications.The different steps for utilizing SQLite are
1.
2.
3.
4.
5.
6.

Create a database.
Open the database.
Create a table.
Create an insert interface for datasets.
Create a query interface for datasets.
Close the database.

The next recipe provides a general method to accomplish these steps.

Recipe: Creating a Separate Database Package


A good modular structure to classes is essential for more complicated Android projects.
Here, the database class is put in its own package com.cookbook.data so it is easy to
reuse.This package contains three classes: MyDB, MyDBhelper, and Constants.
The MyDB class is shown in Listing 9.9. It contains a SQLiteDatabase instance and a
MyDBhelper class (described in the following) with the methods that follow:
n
n

n
n

MyDB()Initializes

a MyDBhelper instance (the constructor).


open()Initializes a SQLiteDatabase instance using the MyDBhelper.This opens a
writeable database connection. If SQLite throws any exception, it tries to get a
readable database instead.
close()Closes

the database connection.


insertdiary()Saves a diary entry to the database as name-value pairs in a
ContentValues instance, and then passes the data to the SQLitedatabase instance
to do an insert.

Download at www.wowebook.com

SQLite Database

233

getdiaries()Reads

the diary entries from the database, saves them in a Cursor


class, and returns it from the method.

Listing 9.9

src/com/cookbook/data/MyDB.java

package com.cookbook.data;
import
import
import
import
import
import

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

public class MyDB {


private SQLiteDatabase db;
private final Context context;
private final MyDBhelper dbhelper;
public MyDB(Context c){
context = c;
dbhelper = new MyDBhelper(context, Constants.DATABASE_NAME, null,
Constants.DATABASE_VERSION);
}
public void close()
{
db.close();
}
public void open() throws SQLiteException
{
try {
db = dbhelper.getWritableDatabase();
} catch(SQLiteException ex) {
Log.v("Open database exception caught", ex.getMessage());
db = dbhelper.getReadableDatabase();
}
}
public long insertdiary(String title, String content)
{
try{
ContentValues newTaskValue = new ContentValues();
newTaskValue.put(Constants.TITLE_NAME, title);
newTaskValue.put(Constants.CONTENT_NAME, content);
newTaskValue.put(Constants.DATE_NAME,
java.lang.System.currentTimeMillis());
return db.insert(Constants.TABLE_NAME, null, newTaskValue);
} catch(SQLiteException ex) {

Download at www.wowebook.com

234

Chapter 9 Data Storage Methods

Log.v("Insert into database exception caught",


ex.getMessage());
return -1;
}
}
public Cursor getdiaries()
{
Cursor c = db.query(Constants.TABLE_NAME, null, null,
null, null, null, null);
return c;
}
}

The MyDBhelper class extends SQLiteOpenHelper and is shown in Listing 9.10.The


SQLiteOpenHelper framework provides methods to manage database creation and
upgrades.The database is initialized in the class constructor MyDBhelper().This requires
the context and database name to be specified for creation of the database file under
/data/data/com.cookbook.datastorage/databases and database schema version to
determine whether the onCreate() or onUpgrade() method is called.
Tables can be added in the onCreate() method using a custom SQL command such as:
create table MyTable (key_id integer primary key autoincrement,
title text not null, content text not null,
recorddate long);

Whenever a database needs to be upgraded (when a user downloads a new version of an


application, for example), the change in database version number calls the onUpgrade()
method.This can be used to alter or drop tables as needed to update the tables to the new
schema.
Listing 9.10

src/com/cookbook/data/MyDBhelper.java

package com.cookbook.data;
import
import
import
import
import
import

android.content.Context;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteException;
android.database.sqlite.SQLiteOpenHelper;
android.database.sqlite.SQLiteDatabase.CursorFactory;
android.util.Log;

public class MyDBhelper extends SQLiteOpenHelper{


private static final String CREATE_TABLE="create table "+
Constants.TABLE_NAME+" ("+
Constants.KEY_ID+" integer primary key autoincrement, "+
Constants.TITLE_NAME+" text not null, "+
Constants.CONTENT_NAME+" text not null, "+
Constants.DATE_NAME+" long);";

Download at www.wowebook.com

SQLite Database

235

public MyDBhelper(Context context, String name, CursorFactory factory,


int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.v("MyDBhelper onCreate","Creating all the tables");
try {
db.execSQL(CREATE_TABLE);
} catch(SQLiteException ex) {
Log.v("Create table exception", ex.getMessage());
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
Log.w("TaskDBAdapter", "Upgrading from version "+oldVersion
+" to "+newVersion
+", which will destroy all old data");
db.execSQL("drop table if exists "+Constants.TABLE_NAME);
onCreate(db);
}
}

The third file of the com.cookbook.data package is the Constants class shown in
Listing 9.11.This class is used to hold all the String constants because they are utilized in
both MyDB and MyDBhelper.
Listing 9.11

src/com/cookbook/data/Constants.java

package com.cookbook.data;
public class Constants {
public static final String DATABASE_NAME="datastorage";
public static final int DATABASE_VERSION=1;
public static final String TABLE_NAME="diaries";
public static final String TITLE_NAME="title";
public static final String CONTENT_NAME="content";
public static final String DATE_NAME="recorddate";
public static final String KEY_ID="_id";
}

Download at www.wowebook.com

236

Chapter 9 Data Storage Methods

Recipe: Using a Separate Database Package


This recipe demonstrates SQLite data storage utilizing the previous recipes database
package. It also ties together the login screen from the Changing the UI Based on Stored
Data recipe and enables the creation and listing of personal diary entries. First, a layout
XML file for creating diary entriesdiary.xmlis shown in Listing 9.12 with its output
screen shown in Figure 9.3.
Listing 9.12

res/layout/diary.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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Diary Title"
/>
<EditText
android:id="@+id/diarydescriptionText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Content"
/>
<EditText
android:id="@+id/diarycontentText"
android:layout_width="fill_parent"
android:layout_height="200dp"
/>
<Button
android:id="@+id/submitButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="submit"
android:textSize="20dp"
/>
</LinearLayout>

Download at www.wowebook.com

SQLite Database

Figure 9.3

237

The diary entry creation screen.

The main activity is Diary.java, shown in Listing 9.13.The com.cookbook.data package


needs to be imported, and the MyDB object is declared, initialized, and opened for use. It also
displays the diary.xml layout and handles the submit button press to save data to the database.
Listing 9.13

src/com/cookbook/datastorage/Diary.java

package com.cookbook.datastorage;
import
import
import
import
import
import
import

android.app.Activity;
android.content.Intent;
android.os.Bundle;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
android.widget.EditText;

import com.cookbook.data.MyDB;
public class Diary extends Activity {
EditText titleET, contentET;
Button submitBT;
MyDB dba;
@Override
public void onCreate(Bundle savedInstanceState) {

Download at www.wowebook.com

238

Chapter 9 Data Storage Methods

super.onCreate(savedInstanceState);
setContentView(R.layout.diary);
dba = new MyDB(this);
dba.open();
titleET = (EditText)findViewById(R.id.diarydescriptionText);
contentET = (EditText)findViewById(R.id.diarycontentText);
submitBT = (Button)findViewById(R.id.submitButton);
submitBT.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
try {
saveItToDB();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void saveItToDB() {
dba.insertdiary(titleET.getText().toString(),
contentET.getText().toString());
dba.close();
titleET.setText("");
contentET.setText("");
Intent i = new Intent(Diary.this, DisplayDiaries.class);
startActivity(i);
}
}

The DataStorage.java class is the same as in Listing 9.6 with the MyPreferences.class
changed to launch the Diary.class when the login is successful:
Toast.makeText(DataStorage.this, "login passed!!",
Toast.LENGTH_SHORT).show();
Intent i = new Intent(DataStorage.this, Diary.class);
startActivity(i);

Finally, the AndroidManifest XML file must be updated to include the new activities, as
shown in Listing 9.14.
Listing 9.14

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cookbook.datastorage"
android:versionCode="1" android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">

Download at www.wowebook.com

SQLite Database

239

<activity android:name=".DataStorage"
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=".MyPreferences" />
<activity android:name=".Diary"/>
</application>
<uses-sdk android:minSdkVersion="7" />
</manifest>

Now that a separate database has been integrated, the layout for the list of entries is discussed in the next recipe to complete the diary application.

Recipe: Creating a Personal Diary


This recipe leverages the ListView object to display multiple entries from a SQLite database table. It shows these items in a vertically scrolling list.The ListView needs a data
adapter to tell the View whenever the underlying data changes.Two XML files need to be
created: diaries.xml, which populates the ListView shown in Listing 9.15, and
diaryrow.xml, which populates the row inside the ListView shown in Listing 9.16.
Listing 9.15

res/layout/diaries.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">
<ListView
android:layout_width="fill_parent" android:dividerHeight="1px"
android:layout_height="fill_parent"
android:id="list>
</ListView>
</LinearLayout>

Listing 9.16

res/layout/diaryrow.xml

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content" android:orientation="vertical"
android:layout_alignLeft="@+id/name" android:layout_below="@+id/name"
xmlns:android="http://schemas.android.com/apk/res/android"

Download at www.wowebook.com

240

Chapter 9 Data Storage Methods

android:padding="12dip">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/name"
android:layout_marginRight="4dp" android:text="Diary Title "
android:textStyle="bold" android:textSize="16dip" />
<TextView android:id="@+id/datetext"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Date Recorded"
android:textSize="14dip" />
</RelativeLayout>

The activity DisplayDiaries.java extends ListActivity to display a ListView. Inside


this class are two inner classes defined: MyDiary is a data class to hold the content of the
diary entry (title, content, and date), and DiaryAdapter is a BaseAdapter class to handle
data retrieval from the database (using getdata()).The following methods are derived
from BaseAdapter and called by ListView:
n
n
n
n

getCount()Returns

how many items on the adapter


getItem()Returns the item specified
getItemID()Returns the ID of item (for this example, there is no item ID)
getView()Returns a view for each item

Note that ListView calls getView() to draw the view for each item.To improve the UI
rendering performance, the view returned by getView() should be recycled as much as
possible.This is done by creating a ViewHolder class to hold the views.
When getView() is called, the view currently displayed to the user is also passed in,
which is when it is saved in the ViewHolder and tagged. On subsequent calls to
getView() with the same view, the tag identifies the view as already in the ViewHolder. In
this case, the content can be changed on the existing view rather than create a new one.
The main activity is shown in Listing 9.17, and the resulting view of diary entries in a
ListView is shown in Figure 9.4.
Listing 9.17

src/com/cookbook/datastorage/DisplayDiaries.java

package com.cookbook.datastorage;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import
import
import
import
import
import

android.app.ListActivity;
android.content.Context;
android.database.Cursor;
android.os.Bundle;
android.view.LayoutInflater;
android.view.View;

Download at www.wowebook.com

SQLite Database

241

import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.cookbook.data.Constants;
import com.cookbook.data.MyDB;
public class DisplayDiaries extends ListActivity {
MyDB dba;
DiaryAdapter myAdapter;
private class MyDiary{
public MyDiary(String t, String c, String r){
title=t;
content=c;
recorddate=r;
}
public String title;
public String content;
public String recorddate;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
dba = new MyDB(this);
dba.open();
setContentView(R.layout.diaries);
super.onCreate(savedInstanceState);
myAdapter = new DiaryAdapter(this);
this.setListAdapter(myAdapter);
}
private class DiaryAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<MyDiary> diaries;
public DiaryAdapter(Context context) {
mInflater = LayoutInflater.from(context);
diaries = new ArrayList<MyDiary>();
getdata();
}
public void getdata(){
Cursor c = dba.getdiaries();
startManagingCursor(c);
if(c.moveToFirst()){
do{
String title =
c.getString(c.getColumnIndex(Constants.TITLE_NAME));

Download at www.wowebook.com

242

Chapter 9 Data Storage Methods

String content =
c.getString(c.getColumnIndex(Constants.CONTENT_NAME));
DateFormat dateFormat =
DateFormat.getDateTimeInstance();
String datedata = dateFormat.format(new
Date(c.getLong(c.getColumnIndex(
Constants.DATE_NAME))).getTime());
MyDiary temp = new MyDiary(title,content,datedata);
diaries.add(temp);
} while(c.moveToNext());
}
}
@Override
public int getCount() {return diaries.size();}
public MyDiary getItem(int i) {return diaries.get(i);}
public long getItemId(int i) {return i;}
public View getView(int arg0, View arg1, ViewGroup arg2) {
final ViewHolder holder;
View v = arg1;
if ((v == null) || (v.getTag() == null)) {
v = mInflater.inflate(R.layout.diaryrow, null);
holder = new ViewHolder();
holder.mTitle = (TextView)v.findViewById(R.id.name);
holder.mDate = (TextView)v.findViewById(R.id.datetext);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.mdiary = getItem(arg0);
holder.mTitle.setText(holder.mdiary.title);
holder.mDate.setText(holder.mdiary.recorddate);
v.setTag(holder);
return v;
}
public class ViewHolder {
MyDiary mdiary;
TextView mTitle;
TextView mDate;
}
}
}

Download at www.wowebook.com

Content Provider

Figure 9.4

243

The ListView of diary entries.

Content Provider
Every application has its own sandbox and cannot access data from other applications. If
access to functions not provided by its own sandbox is required, the application must
explicitly declare permission upfront before installation.Android provides an interface
called ContentProvider to act as a bridge between applications, enabling them to share
and change each others data.A content provider allows a clean separation between the
application layer and data layer. It requires a permission setting in the AndroidManifest
XML file and can be accessed using a simple URI model.
Some native databases Android makes available as content providers are
n
n
n

BrowserRead or modify bookmarks, browser history, or web searches.


CallLogView or update the call history.
ContactsRetrieve, modify, or store the personal contacts. Contact information is
stored in a three-tier data model of tables under a ContactsContract object:
n
ContactsContract.DataContains all kinds of personal data.There is a
predefined set of common data, such as phone numbers and email addresses,
but the format of this table can be application-specific.
n

ContactsContract.RawContactsContains

a set of Data objects associated

with a single account or person.

Download at www.wowebook.com

You might also like