Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Oracle Database wikipedia , lookup
Open Database Connectivity wikipedia , lookup
Entity–attribute–value model wikipedia , lookup
Extensible Storage Engine wikipedia , lookup
Microsoft Jet Database Engine wikipedia , lookup
Concurrency control wikipedia , lookup
Functional Database Model wikipedia , lookup
Relational model wikipedia , lookup
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 n 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 recipe’s 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 entries—diary.xml—is 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 other’s 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 Browser—Read or modify bookmarks, browser history, or web searches. CallLog—View or update the call history. Contacts—Retrieve, modify, or store the personal contacts. Contact information is stored in a three-tier data model of tables under a ContactsContract object: n ContactsContract.Data—Contains 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.RawContacts—Contains a set of Data objects associated with a single account or person. Download at www.wowebook.com