Android Chapter09 Files Preferences SQLite
Android Chapter09 Files Preferences SQLite
Persistence:
Files & Preferences
SQL Databases
Files & Preferences
Android Files
Persistence is a strategy that allows the reusing of volatile objects and other
data items by storing them Into a permanent storage system such as disk files
and databases.
Permanent files can be stored internally in the device’s main memory (usually
small, but not volatile) or externally in the much larger SD card.
2
Files & Preferences
Exploring Android’s File System
Use the emulator’s File Explorer to see and
manage your device’s storage structure.
Internal
Main Memory
External
SD Card
3
Files & Preferences
Choosing a Persistent Environment
Your permanent data storage destination is usually determined by parameters
such as:
• size (small/large),
• location (internal/external),
• accessibility (private/public).
4
Files & Preferences
Shared Preferences
SharedPreferences files are good for handling a handful of Items. Data in this
type of container is saved as <Key, Value> pairs where the key is a string and
its associated value must be a primitive data type. KEY VALUE
5
Files & Preferences
Shared Preferences
Using Preferences API calls
Each of the Preference mutator methods carries a typed-value content that
can be manipulated by an editor that allows putXxx… and getXxx… commands
to place data in and out of the Preference container.
7
Files & Preferences
Shared Preferences. Example - Comments
1. The method getSharedPreferences(…) creates (or retrieves) a table
called my_preferred_choices file, using the default MODE_PRIVATE
access. Under this access mode only the calling application can operate
on the file.
3. The method getXXX(…) is used to extract a value for a given key. If no key
exists for the supplied name, the method uses the designated default
value. For instance myPrefs.getString("chosenColor", "BLACK")
looks into the file myPrefs for the key “chosenColor” to returns its value,
however if the key is not found it returns the default value “BLACK”.
8
Files & Preferences
Shared Preferences. Example - Comments
SharedPreference containers are saved as XML files in the application’s
internal memory space. The path to a preference files is
/data/data/packageName/shared_prefs/filename.
If you pull the file from the device, you will see the following
9
Files & Preferences
Internal Storage. Reading an Internal Resource File
An Android application may include resource elements such as those in:
res/drawable , res/raw, res/menu, res/style, etc.
Resources could be accessed through the .getResources(…) method. The method’s
argument is the ID assigned by Android to the element in the R resource file. For example:
InputStream is = this.getResources()
.openRawResource(R.raw.my_text_file);
} catch (IOException e) {
txtMsg.setText( "Problems: " + e.getMessage() );
}
}// onCreate
11
Files & Preferences
Example 1. Reading an Internal Resource File 2 of 2
Reading an embedded file containing lines of text.
public void PlayWithRawFiles() throws IOException {
String str="";
StringBuffer buf = new StringBuffer();
if (is!=null) {
while ((str = reader.readLine()) != null) {
buf.append(str + "\n" );
3
}
}
reader.close();
is.close();
txtMsg.setText( buf.toString() );
}// PlayWithRawFiles
} // File1Resources
12
Files & Preferences
Example1 - Comments
1. A raw file is an arbitrary dataset stored in its original raw format
(such as .docx, pdf, gif, jpeg, etc). Raw files can be accessed through an
InputStream acting on a R.raw.filename resource entity.
CAUTION: Android requires resource file names to be in lowercase form.
13
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 1 of 6
14
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 2 of 6
In our example the files notes.txt is stored in the phone’s
The internal resource file internal memory under the name:
/data/data/cis470.matos.fileresources/files/notes.txt
(notes.txt) is private and
cannot be seen by other apps
residing in main memory.
15
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 3 of 6
16
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 4 of 6
public class File2WriteRead extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
txtMsg = (EditText) findViewById(R.id.txtMsg);
}// onCreate
17
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 5 of 6
public void onStart() {
super.onStart();
try {
1 InputStream inputStream = openFileInput(FILE_NAME);
if (inputStream != null) {
inputStream.close();
txtMsg.setText(stringBuffer.toString());
}
}
catch ( Exception ex ) {
Toast.makeText(CONTEXT, ex.getMessage() , 1).show();
}
}// onStart 18
Files & Preferences
Example 2. Reading /Writing an Internal Resource File 6 of 6
2. A BufferedReader object moves data line by line from the input file to a
textbox. After the buffer is emptied the data sources are closed.
3. An OutputStreamWriter takes the data entered by the user and send this
stream to an internal file. The method openFileOutput() opens a
private file for writing and creates the file if it doesn't already exist. The
file’s path is: /data/data/packageName/FileName
20
Files & Preferences
Reading /Writing External SD Files
21
Files & Preferences
Reading /Writing External SD Files
22
Files & Preferences
Reading /Writing External SD Files
Although you may use the specific path to an SD file, such as:
mnt/sdcard/mysdfile.txt
WARNING
When you deal with external files you need to request permission to read and write to
the SD card. Add the following clauses to your AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
23
Files & Preferences
Reading /Writing External SD Files
From Android 6.0 (API Level 23), app needs to ask permission from user.
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[]
permissions, int[] grantResults) {
if (requestCode == 1234)
if (grantResults[0] != PackageManager.PERMISSION_GRANTED)
Log.v("TAG", "Permission Denied");
}
24
Files & Preferences
Reading /Writing External SD Files
Detect path for emulated and real SD card
Result in logcat:
/storage/emulated/0/Android/data/vn.edu.hust.myapplication/files
/storage/4CFC-8D04/Android/data/vn.edu.hust.myapplication/files
25
Files & Preferences
Example 3. Reading /Writing External SD Files
This app accepts a few lines of user input and writes it to the
external SD card. User clicks on buttons to either have the
data read and brought back, or terminate the app.
Using DDMS
File Explorer
to inspect the
SD card.
26
Files & Preferences
Example 3. Reading /Writing External SD Files
Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget28"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" <Button
android:orientation="vertical" > android:id="@+id/btnClearScreen"
android:layout_width="160dp"
<EditText android:layout_height="wrap_content"
android:id="@+id/txtData" android:text="2. Clear Screen" />
android:layout_width="match_parent"
android:layout_height="180dp" <Button
android:layout_margin="10dp" android:id="@+id/btnReadSDFile"
android:background="#55dddddd" android:layout_width="160dp"
android:padding="10dp" android:layout_height="wrap_content"
android:gravity="top" android:text="3. Read SD File" />
android:hint=
"Enter some lines of data here..." <Button
android:textSize="18sp" /> android:id="@+id/btnFinish"
<Button android:layout_width="160dp"
android:id="@+id/btnWriteSDFile" android:layout_height="wrap_content"
android:layout_width="160dp" android:text="4. Finish App" />
android:layout_height="wrap_content"
android:text="1. Write SD File" /> </LinearLayout>
27
25
Files & Preferences
Example 3. Reading /Writing External SD Files 1 of 4
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
28
Files & Preferences
Example 3. Reading /Writing External SD Files 2 of 4
myOutWriter.append(txtData.getText());
myOutWriter.close();
Toast.makeText(getBaseContext(),
"Done writing SD 'mysdfile.txt'",
Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
}// onClick
}); // btnWriteSDFile
29
Files & Preferences
Example 3. Reading /Writing External SD Files 3 of 4
}// onCreate
}// File3SdCard
31
SQL Databases
Using SQL databases in Andorid
Included into the core Android architecture there is an standalone Database
Management System (DBMS) called SQLite which can be used to:
Create a database,
Define
SQL tables,
indices,
queries,
views,
triggers
Insert rows,
Delete rows,
Change rows,
Run queries and
Administer a SQLite database file.
32
SQL Databases
Characteristics of SQLite
• Transactional SQL database engine.
• Small footprint (less than 400KBytes)
• Serverless
• Zero-configuration
• The source code for SQLite is in the public domain.
• According to their website, SQLite is the most widely deployed SQL
database engine in the world .
Reference:
http://sqlite.org/index.html
33
SQL Databases
Creating a SQLite database - Method 1
SQLiteDatabase.openDatabase( myDbPath,
null,
SQLiteDatabase.CREATE_IF_NECESSARY);
If the database does not exist then create a new one. Otherwise, open the
existing database according to the flags:
OPEN_READWRITE, OPEN_READONLY, CREATE_IF_NECESSARY .
Parameters
path to database file to open and/or create
factory an optional factory class that is called to instantiate a cursor when
query is called, or null for default
flags to control database access mode
Path:
/data/data/cis470.matos.sqldatabases/
Where: cis470.matos.sqldatabases
is the package’s name
36
SQL Databases
Example1: Creating a SQLite database on the SD card
Using:
SQLiteDatabase db;
String SDcardPath = Environment
.getExternalStorageDirectory()
.getPath() + "/myfriends";
db = SQLiteDatabase.openDatabase(
SDcardPath,
null,
SQLiteDatabase.CREATE_IF_NECESSARY
);
37
SQL Databases
Sharing Limitations
Warning
• Databases created in the internal /data/data/package space
are private to that package.
• You cannot access internal databases belonging to other people
(instead use Content Providers or external SD resident DBs).
SQLiteDatabase db = this.openOrCreateDatabase(
"myfriendsDB",
MODE_PRIVATE,
null);
Once created, the SQLite database is ready for normal operations such as:
creating, altering, dropping resources (tables, indices, triggers, views, queries
etc.) or administrating database resources (containers, users, …).
Action queries and Retrieval queries represent the most common operations
against the database.
40
SQL Databases
Transaction Processing
Transactions are desirable because they help maintaining consistent data and
prevent unwanted data losses due to abnormal termination of execution.
This notion is called: atomicity to reflect that all parts of a method are fused
in an indivisible ‘statement’.
41
SQL Databases
Transaction Processing
The typical Android’s way of running transactions on a SQLiteDatabase is
illustrated by the following code fragment (Assume db is a SQLiteDatabase)
db.beginTransaction();
try {
//perform your database operations here ...
db.setTransactionSuccessful(); //commit your changes
}
catch (SQLiteException e) {
//report problem
}
finally {
db.endTransaction();
}
The SQL syntax used for creating and populating a table is illustrated in the
following examples
The autoincrement value for recID is NOT supplied in the insert statement as it is
internally assigned by the DBMS.
43
SQL Databases
Example 2. Create and Populate a SQL Table
• Our Android app will use the execSQL(…) method to manipulate SQL
action queries. The example below creates a new table called tblAmigo.
• The table has three fields: a numeric unique identifier called recID, and
two string fields representing our friend’s name and phone.
• If a table with such a name exists it is first dropped and then created
again.
• Finally three rows are inserted in the table.
Note: For presentation economy we do not show the
entire code which should include a transaction frame.
44
SQL Databases
Example 2. Create and Populate a SQL Table
• After executing the previous code snippet, we transfered the database to
the developer’s file system and used the SQL-ADMINISTRATION tool.
• There we submitted the SQL-Query: select * from tblAmigo.
• Results are shown below.
45
SQL Databases
Example 2. Create and Populate a SQL Table
Comments
1. The field recID is defined as the table’s PRIMARY KEY.
3. On par with other SQL systems, SQLite offers the data types: text,
varchar, integer, float, numeric, date, time, timestamp, blob, boolean.
Caution:
You should call the execSQL method inside of a try-catch-finally block. Be
aware of potential SQLiteException conflicts thrown by the method.
46
SQL Databases
Example 2. Create and Populate a SQL Table
NOTE:
RowID
SQLITE uses an invisible field called ROWID to 0
uniquely identify each row in each table. 1
2
Consequently in our example the field recID and
the database ROWID are functionally similar. 3
4
5
47
SQL Databases
Asking Questions - SQL Queries
1. Retrieval queries are known as SQL-select statements.
2. Answers produced by retrieval queries are always held
in a table.
3. In order to process the resulting table rows, the user
should provide a cursor device. Cursors allow a row-at-
the-time access mechanism on SQL tables.
where ( restriction-join-conditions )
order by fieldn1, …, fieldnm
group by fieldm1, … , fieldmk
having (group-condition)
Example B.
50
SQL Databases
Example3. Using a Parameterless RawQuery (version 1)
Consider the following code fragment
Cursor c1 = db.rawQuery("select * from tblAMIGO", null);
51
SQL Databases
Example3. Using a Parametized RawQuery (version 2)
Passing arguments.
Assume we want to count how many friends are there whose name is ‘BBB’
and their recID > 1. We could use the following solution:
53
SQL Databases
SQL Cursors
Cursors are used to gain sequential & random access to
tables produced by SQL select statements.
c1.moveToPosition(-1);
2
while ( c1.moveToNext() ){
1. Prepare a rawQuery passing a simple sql statement with no arguments, catch the
resulting tuples in cursor c1.
2. Move the fetch marker to the absolute position prior to the first row in the file.
The valid range of values is -1 <= position <= count.
3. Use moveToNext() to visit each row in the result set
55
SQL Databases
Example 4B. Traversing a Cursor – Enhanced Navigation 1 of 2
1 private String showCursor( Cursor cursor) {
// reset cursor's top (before first row)
cursor.moveToPosition(-1);
String cursorData = "\nCursor: [";
try {
// get SCHEMA (column names & types)
2 String[] colName = cursor.getColumnNames();
for(int i=0; i<colName.length; i++){
String dataType = getColumnType(cursor, i);
cursorData += colName[i] + dataType;
if (i<colName.length-1){
cursorData+= ", ";
}
}
} catch (Exception e) {
Log.e( "<<SCHEMA>>" , e.getMessage() );
}
cursorData += "]";
56
SQL Databases
Example 4B. Traversing a Cursor – Enhanced Navigation 2 of 2
3 while (cursor.moveToNext()) {
String cursorRow = "\n[";
for (int i = 0; i < cursor.getColumnCount(); i++) {
4 cursorRow += cursor.getString(i);
if (i<cursor.getColumnCount()-1)
cursorRow += ", ";
}
cursorData += cursorRow + "]";
}
return cursorData + "\n";
}
3. The method moveToNext forces the cursor to travel from its current
position to the next available row.
5. The function .getColumnType() provides the data type of the current field
(0:null, 1:int, 2:float, 3:string, 4:blob)
58
SQL Databases
SQLite Simple Queries - Template Based Queries
Simple SQLite queries use a template oriented schema whose goal is to ‘help’
non-SQL developers in their process of querying a database.
60
SQL Databases
Example5. SQLite Simple Queries
Assume we need to consult an EmployeeTable (see next Figure) and find the
average salary of female employees supervised by emp. 123456789. Each
output row consists of Dept. No, and ladies-average-salary value. Our output
should list the highest average first, then the second, and so on. Do not include
depts. having less than two employees.
61
SQL Databases
Example5. SQLite Simple Queries
62
SQL Databases
Example6. SQLite Simple Queries
In this example we use the tblAmigo table. We are interested in selecting the
columns: recID, name, and phone. The condition to be met is that RecID must
be greater than 2, and names must begin with ‘B’ and have three or more
letters.
String [] columns = {"recID", "name", "phone"};
Cursor c1 = db.query (
"tblAMIGO",
columns,
"recID > 2 and length(name) >= 3 and name like 'B%' ",
null, null, null,
"recID" );
We enter null in each component not supplied to the method. For instance,
in this example select-args, having, and group-by are not used. 63
SQL Databases
Example7. SQLite Simple Queries
In this example we will construct a more complex SQL select statement.
We are interested in tallying how many groups of friends whose recID > 3
have the same name. In addition, we want to see ‘name’ groups having
no more than four people each.
64
SQL Databases
Example7. SQLite Simple Queries
An equivalent Android-SQLite solution using a simple template query follows.
1 String [] selectColumns = {"name", "count(*) as TotalSubGroup"};
2 String whereCondition = "recID > ? ";
String [] whereConditionArgs = {"3"};
3 String groupBy = "name";
String having = "count(*) <= 4";
String orderBy = "name";
65
SQL Databases
Example7. SQLite Simple Queries
Observations
1. The selectColumns string array contains the output fields. One of them
(name) is already part of the table, while TotalSubGroup is an alias for the
computed count of each name sub-group.
66
SQL Databases
SQL Action Queries
Action queries are the SQL way of performing maintenance operations on
tables and database resources. Example of action-queries include: insert,
delete, update, create table, drop, etc.
Examples:
insert into tblAmigos
values ( ‘Macarena’, ‘555-1234’ );
update tblAmigos
set name = ‘Maria Macarena’
where phone = ‘555-1234’;
67
SQL Databases
SQLite Action Queries Using: ExecSQL
Perhaps the simplest Android way to phrase a SQL action query is to ‘stitch’
together the pieces of the SQL statement and give it to the easy to use –but
rather limited- execSQL(…) method.
Unfortunately SQLite execSQL does NOT return any data. Therefore knowing
how many records were affected by the action is not possible with this
operator. Instead you should use the Android versions describe in the next
section.
db.execSQL(
"update tblAMIGO set name = (name || 'XXX') where phone >= '555-1111' ");
This statement appends ‘XXX’ to the name of those whose phone number is
equal or greater than ‘555-1111’.
Note
The symbol || is the SQL concatenate operator
68
SQL Databases
SQLite Action Queries Using: ExecSQL cont. 1
The same strategy could be applied to other SQL action-statements such as:
69
SQL Databases
Android’s INSERT, DELETE, UPDATE Operators
• Android provides a number of additional methods to perform insert,
delete, update operations.
• They all return some feedback data such as the record ID of a recently
inserted row, or number of records affected by the action. This format is
recommended as a better alternative than execSQL.
public long insert(String table,
String nullColumnHack,
ContentValues values )
myArgs
ContentValues myArgs= new ContentValues(); Key Value
name ABC
myArgs.put("name", "ABC");
myArgs.put("phone", "555-7777"); phone 555-7777
71
SQL Databases
Android’s INSERT Operation
public long insert(String table, String nullColumnHack, ContentValues values)
The method tries to insert a row in a table. The row’s column-values are
supplied in the map called values. If successful, the method returns the rowID
given to the new record, otherwise -1 is sent back.
Parameters
table the table on which data is to be inserted
nullColumnHack Empty and Null are different things. For instance, values could be
defined but empty. If the row to be inserted is empty (as in our
next example) this column will explicitly be assigned a NULL
value (which is OK for the insertion to proceed).
72
SQL Databases
Android’s INSERT Operation
rowValues.put("name", "ABC");
rowValues.put("phone", "555-1010");
2 long rowPosition = db.insert("tblAMIGO", null, rowValues);
3 rowValues.put("name", "DEF");
rowValues.put("phone", "555-2020");
rowPosition = db.insert("tblAMIGO", null, rowValues);
4 rowValues.clear();
73
SQL Databases
Android’s INSERT Operation
Comments
1. A set of <key, values> called rowValues is creted and supplied to the
insert() method to be added to tblAmigo. Each tblAmigo row consists of
the columns: recID, name, phone. Remember that recID is an auto-
incremented field, its actual value is to be determined later by the
database when the record is accepted.
2. The newly inserted record returns its rowID (4 in this example)
3. A second records is assembled and sent to the insert() method for
insertion in tblAmigo. After it is collocated, it returns its rowID (5 in this
example).
4. The rowValues map is reset, therefore rowValues which is not null
becomes empty.
5. SQLite rejects attempts to insert an empty record returning rowID -1.
6. The second argument identifies a column in the database that allows
NULL values (NAME in this case). Now SQL purposely inserts a NULL
value on that column (as well as in other fields, except the key RecId) and
the insertion successfully completes.
74
SQL Databases
Android’s UPDATE Operation
public int update ( String table, ContentValues values,
String whereClause, String[] whereArgs )
The method tries to update row(s) in a table. The SQL set column=newvalue
clause is supplied in the values map in the form of [key,value] pairs.
The method returns the number of records affected by the action.
Parameters
Here are the steps to make the call using Android’s equivalent Update
Method
String [] whereArgs = {"2", "7"};
1
2 updValues.put("name", "Maria");
76
SQL Databases
Android’s UPDATE Operation
Comments
1. Our whereArgs is an array of arguments. Those actual values will replace
the placeholders ‘?’ set in the whereClause.
2. The map updValues is defined and populated. In our case, once a record
is selected for modifications, its “name” field will changed to the new
value “maria”.
3. The db.update() method attempts to update all records in the given table
that satisfy the filtering condition set by the whereClause. After
completion it returns the number of records affected by the update (0 If
it fails).
4. The update filter verifies that "recID > ? and recID < ? ". After the args
substitutions are made the new filter becomes: "recID > 2 and recID < 7".
77
SQL Databases
Android’s DELETE Operation
The method is called to delete rows in a table. A filtering condition and its
arguments are supplied in the call. The condition identifies the rows to be
deleted. The method returns the number of records affected by the action.
Parameters
78
SQL Databases
Android’s DELETE Operation
Example
Consider the following SQL statement:
Delete from tblAmigo where recID > 2 and recID < 7
A record should be deleted if its recID is in between the values 2, and 7. The
actual values are taken from the whereArgs array. The method returns the
number of rows removed after executing the command (or 0 if none).
79