1 of 65

Android Data

Juan E. Vargas

https://goo.gl/OfMONf

2 of 65

Data Storage Options on Android

Reading/Writing data into files is important for obvious reasons.

There are three alternatives for data storage on Android:

  1. Shared Preferences
  2. Files
  3. SQLite DB

3 of 65

Data Storage Options on Android

Relatively small data can be stored as a collection of key-values via the SharedPreferences APIs.

Android file system is based on Linux ext4 and thus it offers functions found in many disk-based file systems.

SQLite is part of the Android Platform. It provides a relational DB for local storage in the device, i.e., it contains tables (rows and columns), indexes, transactions, joins, etc, that together with other features make up a DB schema.

4 of 65

SharedPreferences

Relatively small data can be stored as a collection of key-values (hash tables) via the SharedPreferences APIs.

Hash tables have nice properties: on average space is O(n), search is O(1), insert is O(1) and delete is O(1). Worst case is O(n).

A SharedPreferences object points to a file containing key-value pairs and provides methods to read and write them.

SharedPreferences files are managed by the framework and can be private or shared.

5 of 65

Getting a SharedPreferences Object

getSharedPreferences() can be used to access multiple shared preference files identified by name. This could be used to have a collection of activity preferences by a user.

getPreferences() can be used to open one shared preference file for each activity. This retrieves a default shared preference file that belongs to the activity, therefore you don't need to supply a name.

Just remember that you would be using hash tables

http://developer.android.com/training/basics/data-storage/shared-preferences.html

6 of 65

Working with Files

Android file system is based on Linux ext4 and thus it offers most of the disk-based functions found in most file systems.

File objects are created for reading or writing data in a sequential, start-to-finish order, without skipping around, e.g., image or sound files exchanged over a network.

There are several details worth knowing about where files can be stored to control accessibility. Once you know those details working with files for I/O is done in ways similar to the standard I/O packages in Java.

7 of 65

Working with Files

ACCESS/STORAGE

EXTERNAL

INTERNAL

PRIVATE

PUBLIC

Awareness of access and type of storage is important

8 of 65

Internal Storage

Android devices have two file storage areas: "internal" and "external".

Internal storage: It's always available. By default, only the running app has access to internal storage files.

PROS: It is there.

CONS: It is limited

When users uninstall the app, the system removes all app's files from internal storage.

This is good for security because it ensures that neither the user nor other apps can access other files. This is bad because other apps should not expect those files to be there...

9 of 65

Getting an Internal File Directory

To get a file object representing an internal directory for your app:

getFilesDir()

To get a file object representing an internal directory for your app's temporary cache files:

getCacheDir()

To create a new file in one of those directories:

File file = new File(context.getFilesDir(), filename)

can you guess what the context is?

Context is an interface to global information about an application environment. Context is an abstract class provided by the Android system. It allows access to application-specific resources and classes, including calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

10 of 65

Writing one line of text to internal file

public void writeFile(String sFileName, String sBody) {

try {

File root = new File(Environment.getExternalStorageDirectory(), "Notes");

if (!root.exists()) {

root.mkdirs();

}

File gpxfile = new File(root, sFileName);

FileWriter writer = new FileWriter(gpxfile);

writer.append(sBody);

writer.flush();

writer.close();

} catch (IOException e) {

e.printStackTrace();

Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();

}

}

Looks familiar?

can you guess what the environment is?

11 of 65

Reading text from an internal file

public void readFile( ) {

String locFileName = "enter a file name here";

ArrayList<String> lines = new ArrayList<String>();

try {

BufferedReader in = new BufferedReader(new FileReader(locFileName));

String line;

while ((line = in.readLine()) != null) {

lines.add(line);

}

in.close();

// do something with the content in lines;

} catch (Exception e) {

e.printStackTrace();

Toast.makeText(this, "Error in readFile", Toast.LENGTH_SHORT).show();

}

Toast.makeText(this, "Done", Toast.LENGTH_SHORT).show();

}

Looks familiar?

12 of 65

Pretty Simple, Right?

13 of 65

Runtime Permissions

Yes, the rules of the game have changed, again…

In the past, apps needed to obtain permissions at install time. From Android 6.0 (API level 23) and forward, apps must obtain permissions at runtime. This approach:

1. Streamlines the app install process; users don’t need to grant permissions at install/update time.

2. Gives users more control, e.g., depending on the circumstances (in a museum? in a park?) an app may need to access the camera but not the device location.

3. Users may revoke permissions at any time via the app's Settings screen.

14 of 65

Permission Groups

The URL below points to the list of Normal

and Dangerous Permissions:

https://developer.android.com/guide/topics/permissions/requesting.html#normal-dangerous

READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE belong to the Dangerous group.

Therefore the user must give explicit approval. More about that topic can be found at this URL:

Android Permissions

15 of 65

External Storage (ES)

ES is the best place for files with no access restrictions, i.e., for files that you want to share with other apps, or files that users will access with an external computer.

ES is not always available; users can mount the ES as USB and in some cases remove from device.

ES is world-readable, therefore files saved as ES may be read outside of the app control.

When a user uninstalls an app, the system removes the app's files only if you save them in the app directory using getExternalFilesDir().

16 of 65

Obtaining Permission

Apps need to request access for read, write, or both.

This is done in the manifest file:

<manifest ...>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

...

</manifest>

17 of 65

Save to External Storage

// is external storage available for read/write?

public boolean isExternalStorageWritable() {

String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {

return true;

}

return false;

}

// is external storage available for read ?

public boolean isExternalStorageReadable() {

String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state) ||

Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {

return true;

}

return false;

}

18 of 65

Public or Private External Files

External files can be Public or Private.

This distinction is not necessary for internal files

19 of 65

External Public Files

public File getAlbumStorageDir(String albumName) {

// Get the directory for the user's public pictures directory.

File file = new File(Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_PICTURES), albumName);

if (!file.mkdirs()) {

Log.e(LOG_TAG, "Directory not created");

}

return file;

}

Once you have the files open for I/O you can use the standard I/O packages in Java.

20 of 65

External Private Files

public File getAlbumStorageDir(Context context, String albumName) {

// Get the directory for the app's private pictures directory.

File file = new File(context.getExternalFilesDir(

Environment.DIRECTORY_PICTURES), albumName);

if (!file.mkdirs()) {

Log.e(LOG_TAG, "Directory not created");

}

return file;

}

Note that “private” is the default

Once you have the files open for I/O you can use the standard I/O packages in Java.

21 of 65

Android SQLite

SQLite is part of the Android Platform. It provides a relational DB for local storage in the device, i.e., it contains tables (rows and columns), indexes, etc, that together with other features make up a DB schema.

SQLite was designed to operate as an embedded DBMS running under a very small footprint (~350KB) within a single cross-platform disk file.

SQLite implements most of the SQL92 standard and supports “ACIDtransactions:

Atomic, Consistent, Isolated, Durable

As of this writing Android ships with SQLite version 3.7

22 of 65

SQL ACID

Atomicity: Requires that each transaction be "all or nothing": if one part of the transaction fails, the entire transaction fails, and the DB state is left unchanged.

Consistency: Any transaction will bring the DB from one valid state to another. Any data written to the DB must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This does not guarantee correctness of the transaction.

Isolation: The concurrent execution of transactions results in a system state that would be obtained if transactions were executed serially.

The durability property ensures that once a transaction has been committed, the outcome will remain, even in the event of power loss, crashes, or errors.

23 of 65

Using SQLite3 from Console

Android ships with the Sqlite3 DB and can be used from the Android shell (command) window (adb). Here are the steps

  1. Find where the adb resides
    1. C:\Users\jvargas\AppData\Local\Android\android-sdk\platform-tools\adb.exe
    2. sudo apt-get install android-tools-adb
  2. Connect to device, via emulator or USB.
  3. If using Genymotion, ensure that emulator is set to use the android sdk, not Genymotion’s
  4. From the path in 1.1, or, 1.2, or from the Android Studio Terminal issue commands
    • adb devices
    • adb shell
    • sqlite3
    • …. issue sqlite commands...

24 of 65

3

2

1

juanbuntu

GSU Laptop

~/AndroidStudioProjects/Sdk/platform-tools/...

/home/juan/AndroidStudioProjects/sdk

25 of 65

Solus

cd /home/juan/Android/Sdk/platform-tools

to execute (I don’t want to mess up my $PATH variable)

/home/juan/Android/Sdk/platform-tools/adb shell

/home/juan/Android/Sdk/platform-tools/sqlite3

… etc …

look for Sdk/platform-tools

look for Sdk/platform-tools

26 of 65

4

5

27 of 65

Nice but the real power is to use sqlite in code.

6

7

CREATE Table cars ( Id INT, Name TEXT, Price Int);

INSERT INTO cars VALUES ( 1, 'Audi', 65000);

INSERT INTO cars VALUES ( 2, 'Mercedes', 55000);

INSERT INTO cars VALUES ( 3, 'Saab', 45000);

INSERT INTO cars VALUES ( 4, 'Porshe', 35000);

INSERT INTO cars VALUES ( 6, 'Jaguar', 25000);

INSERT INTO cars VALUES ( 7, 'Honda', 15000);

INSERT INTO cars VALUES ( 8, 'Toyota', 10000);

28 of 65

Android SQLite

Access to an SQLite DB is done via the Android File System.

Developers need to take into consideration the fact that operations must be asynchronous because access to files is typically slow.

For this reason access is often made via AsyncTask, AsyncQueryHandler, CursorLoader...

… Working with DBs in general is not difficult, just excruciatingly boring… because you have to take care of several details… if you need more information I suggests reading:

http://www.vogella.com/tutorials/AndroidSQLite/article.html

http://www.techotopia.com/index.php/An_Android_SQLite_Database_Tutorial

29 of 65

Defining a Schema and a Contract

Schemas are a formal declaration that specifies how a DB is organized.

Given the nature of mobile apps, the Android docs suggest having a companion class (contract) to explicitly specify schema layouts in a systematic and self-documenting way.

Thus a contract is a class acting as a template that defines names for URIs, tables, columns, indexes, etc..

The contract keeps a consistent set of constants across classes in the same package and this helps developers make changes in one place and propagate the changes throughout the code

30 of 65

Sample Contract Class

public final class FeedReaderContract {

// To prevent someone from accidentally instantiating the contract class,

// give it an empty constructor.

public FeedReaderContract() {}

/* Inner class that defines the table contents */

public static abstract class FeedEntry implements BaseColumns {

public static final String TABLE_NAME = "entry";

public static final String COLUMN_NAME_ENTRY_ID = "entryid";

public static final String COLUMN_NAME_TITLE = "title";

public static final String COLUMN_NAME_SUBTITLE = "subtitle";

...

}

}

31 of 65

32 of 65

Typical SQL Statements

private static final String TEXT_TYPE = " TEXT";

private static final String COMMA_SEP = ",";

private static final String SQL_CREATE_ENTRIES =

"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +

FeedEntry._ID + " INTEGER PRIMARY KEY," +

FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +

FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +

... // Any other options for the CREATE command

" )";

private static final String SQL_DELETE_ENTRIES =

"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

FeedEntry is the contract class. Fields in yellow background are part of the contract

If you need a quick review on SQL I recommend looking at this URL:

http://www.w3schools.com/sql/

33 of 65

Where are SQL DBs stored?

Android stores DBs in private disk space associated to the application. In this way the data is secure; by default this area is not accessible to other applications.

The system performs potentially long-running operations for creating and updating the DB only when needed and not during app startup.

The Android architecture defines a class called SQLiteOpenHelper which is used to create a DB and to provide version management.

34 of 65

SQLiteOpenHelper Methods

getDatabaseName( )

getReadableDatabase ( )

getWritableDatabase( )

EVENT Listeners:

onConfigure( )

onCreate( )

onDowngrade ( )

onUpgrade ( )

onOpen( )

35 of 65

Writing into a DB

// Get the data repository in write mode

SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create new map of values; column names are the keys

ContentValues values = new ContentValues();

values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);

values.put(FeedEntry.COLUMN_NAME_TITLE, title);

values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert new row, return primary key value of the new row

long newRowId;

newRowId = db.insert(

FeedEntry.TABLE_NAME,

FeedEntry.COLUMN_NAME_NULLABLE,

values);

36 of 65

Reading

From

DB

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database

// you will actually use after this query.

String[] projection = {

FeedEntry._ID,

FeedEntry.COLUMN_NAME_TITLE,

FeedEntry.COLUMN_NAME_UPDATED,

...

};

// How you want the results sorted in the resulting Cursor

String sortOrder =

FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(

FeedEntry.TABLE_NAME, // The table to query

projection, // The columns to return

selection, // The columns for the WHERE clause

selectionArgs, // The values for the WHERE clause

null, // don't group the rows

null, // don't filter by row groups

sortOrder // The sort order

);

37 of 65

android.database

This is a package that contains several SQLite DBMS classes and interfaces that apps need to use in order to manage DBs with content local to the device.

38 of 65

39 of 65

android.database.SQLiteDatabase

It provides the insert(), update(), & delete() methods

40 of 65

The next slides show some of the packages, classes and interfaces used to develop apps with DBs

41 of 65

android.database.sqlite

42 of 65

android.database.sqlite

android.database.sqlite is a package that contains classes to manage DBs in the device.

To create a db:

SQLiteDatabase aDbObj=openOrCreateDatabase("aDBName",MODE_PRIVATE,null);

Once the DB object is created, SQL statements that return NO DATA can be executed by

aDbObj.execSQL

Take a deep dive into the methods that can be used by objects of this class:

http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html

43 of 65

44 of 65

android.database.sqlite

aDbObj.execSQL

Once a DB is created, tables can be added:

aDbObj.execSQL("CREATE TABLE IF NOT EXISTS aTabName(Username VARCHAR,Password VARCHAR);");

To insert records into a table DO NOT use:

mydatabase.execSQL("INSERT INTO aTabName VALUES('admin','admin');");

and from that point insertion, deletion, etc can be done to the DB and its tables.

The execSQL command does not return data.

45 of 65

46 of 65

Cursor

To get content from a DB and its tables we need a Cursor object.

Cursor is a class interface that provides random R/W access to the result set returned by a DB query. Cursor implementations are not required to be synchronized, therefore code using a Cursor from multiple threads should perform its own synchronization when using a Cursor,

for a detailed list of the methods visit:

http://developer.android.com/reference/android/database/Cursor.html

Cursor resultSet = aDbObj.rawQuery("Select * from aTabName",null);�resultSet.moveToFirst();�String username = resultSet.getString(1);�String password = resultSet.getString(2);

47 of 65

More Cursor Methods

getColumnCount()

Returns the total number of columns in table.

getColumnIndex(String columnName)

Returns the index number of a column by specifying the name of the column

getColumnName(int columnIndex)

Returns the name of the column by specifying the index of the column

getColumnNames()

Returns an array with the column names in the table.

getCount()

Returns the total number of rows in the cursor

getPosition()

Returns the current position of the cursor in the table

isClosed()

Returns true if the cursor is closed, false otherwise

48 of 65

SQLiteOpenHelper

Operations on DBs are transactional by nature. This is why there is a class called SQLiteOpenHelper that automatically manages the creation and updates of DB objects. By using this class you get transaction capabilities as a “free lunch”:

public class DBHelper extends SQLiteOpenHelper {� public DBHelper(){� super(context,DATABASE_NAME,null,1);� }� public void onCreate(SQLiteDatabase db) {}� public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) {}�}

49 of 65

The next slides show a full developed app that:

  1. Creates a SQLite3 DB in a device
  2. Has a UI to enter/modify/delete entries on the DB

50 of 65

Transferring to

51 of 65

AddressBook App

52 of 65

Even the “+” icon needs to be compatible with Android Specs. There is a site on the developers.android site with drawable objects.

another drawable object

53 of 65

54 of 65

Clicking on the names opens a view to view/edit the content

55 of 65

56 of 65

Inside the App

There are 3 classes

  1. MainActivity
  2. DBHelper
  3. DisplayContact

57 of 65

MainActivity

  1. Acts as container for the other two classes
  1. . . oops. .

58 of 65

MainActivity

  • Sets three listeners

1

2

3

59 of 65

DisplayContact

Gets the text from the textView objects (name, phone, eMail, street, place) to the DB and back

60 of 65

DisplayContact

61 of 65

DisplayContact

using cursor to get stuff from DB

62 of 65

dbHelper

63 of 65

dbHelper

64 of 65

65 of 65