s12.androidcookbook.steele.ch9.sqlite
TRANSCRIPT
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
1/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 DatabaseFor 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/
/databases folder of an Android device.A Content Provider can be
used to share the database information between applications.The different steps for utiliz-
ing SQLite are
1. Create a database.
2. Open the database.
3. Create a table.
4. Create an insert interface for datasets.
5. Create a query interface for datasets.
6. 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 SQLiteDatabaseinstance and a
MyDBhelperclass (described in the following) with the methods that follow:
n MyDB()Initializes a MyDBhelper instance (the constructor).
n open()Initializes a
SQLiteDatabaseinstance using the
MyDBhelper.This opens awriteable database connection. If SQLite throws any exception, it tries to get a
readable database instead.
n close()Closes the database connection.
n insertdiary()Saves a diary entry to the database as name-value pairs in a
ContentValues instance, and then passes the data to the SQLitedatabaseinstance
to do an insert.
Download at www.wowebook.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
2/12
233SQLite Database
n 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 android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import 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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
3/12
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 MyDBhelperclass extends SQLiteOpenHelperand is shown in Listing 9.10.The
SQLiteOpenHelperframework provides methods to manage database creation and
upgrades.The database is initialized in the class constructor MyDBhelper().This requiresthe 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 newschema.
Listing 9.10 src/com/cookbook/data/MyDBhelper.java
package com.cookbook.data;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import 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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
4/12
235SQLite Database
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 inListing 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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
5/12
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
Download at www.wowebook.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
6/12
237SQLite Database
Figure 9.3 The diary entry creation screen.
The main activity is Diary.java, shown in Listing 9.13.The com.cookbook.datapackage
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 android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import 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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
7/12
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
Download at www.wowebook.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
8/12
239SQLite Database
Now that a separate database has been integrated, the layout for the list of entries is dis-
cussed 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 data-
base 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
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
9/12
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
10/12
241SQLite Database
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 diaries;
public DiaryAdapter(Context context) {
mInflater = LayoutInflater.from(context);
diaries = new ArrayList();
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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
11/12
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.co
-
8/11/2019 s12.AndroidCookbook.steele.ch9.SQLite
12/12
243Content Provider
Content ProviderEvery 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 mustexplicitly declare permission upfront before installation.Android provides an interface
called ContentProviderto 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 BrowserRead or modify bookmarks, browser history, or web searches.
n CallLogView or update the call history.
n
ContactsRetrieve, modify, or store the personal contacts. Contact information isstored in a three-tier data model of tables under a ContactsContractobject:
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.
Figure 9.4 The ListView of diary entries.