Title: The Android File System
1The Android File System
- Onboard Linux Architecture
- User privileges
- Initially quite limited some directories hidden
- Rooting gives users super user access
- Procedure is different for different devices
- Destroying the operation of the device bricking
- Onboard data Applications have their reserved
storage areas (sandbox) - External data
- SD card or USB connection
- Public shared /mnt/sdcard/
- Writing to external storage has no security
protection
2Managing Data (Alternatives)
- Application Direct Access Read only from res/raw
or assets directories - Web-based Interact through web-URLs to access
cloud-based data - Direct File I/O Read/write files onboard or on
SD cards - Use Standard Java stream and Random Access File
classes - Restriction Onboard file I/O restricted to
application sandbox - Restriction SD card writes requires access
permission - Preferences Key/Value pairs of data
- Database Tables Use the built-in SQL-Lite
database facility - Increase functionality
- Content Providers expose data to other
applications - Services background processes that run detached
from any view
3Application Direct Access
- Static application files
- Custom codecs that are not widely supported
- XML-based configuration data
- Store either in res/raw or in assets
- res/raw enables creating sub-directories for
specific device configurations - Files stored either in res/raw or in assets are
not pre-compiled by Android - Files stored directly in the application are
read-only and cannot be modified - Access using standard Java I/O operationsInputSt
ream is app.getResources().openRawResource(R.raw
.foo) in BufferedReader in new
BufferedReader(new InputStreamReader(is)))Input
Stream rawRes context.getAssets().open(foo.bar"
) Reader r new BufferedReader(new
InputStreamReader(rawRes, "UTF8"))
Note Use lower case alphanumerics to name files
in res/raw
4Downloading Files
- Downloading quickly degrades battery life
- Solutions
- Pre-fetch A single large download has less
impact than multiple smaller downloads - Reuse existing connections rather than
reestablishing them - Schedule regular downloads at the longest
intervals that are possible - Bundle non-time sensitive requests together
5Download XML from Server
Requires ltuses-permission androidname "
android.permission.INTERNET " /gt
- String myFeed getString(R.string.my_feed)
// HTTP Web address - try
- URL url new URL(myFeed) // Create a new
HTTP URL connection - URLConnection connection url.openConnection()
- HttpURLConnection httpConnection
(HttpURLConnection)connection int
responseCode httpConnection.getResponseCode()
- if (responseCode HttpURLConnection.HTTP_OK)
- InputStream in httpConnection.getInputStream(
) - processStream(in) // Use standard Java
DOM parsing classes -
- catch (MalformedURLException e)
Log.d(TAG, "Malformed URL", e) - catch (IOException e) Log.d(TAG, "IO
Exception.", e)
6Download Using Download Manager
- DownloadManager m (DownloadManager)getSystemServi
ce(Context.DOWNLOAD_SERVICE) - long myReference m.enqueue(new
Request(Uri.parse(R.string.webLoc))) - BroadcastReceiver receiver new
BroadcastReceiver() - _at_Override public void onReceive(Context
context, Intent intent) - long reference intent.getLongExtra(Download
Manager.EXTRA_DOWNLOAD_ID, -1) - if (myReference reference)
- Query query new Query()
- Cursor cursor downloadManager.query(query.set
FilterById(reference)) - if (cursor.moveToFirst())
- int fileX cursor.getColumnIndex(DownloadM
anager.COLUMN_LOCAL_FILENAME) - int uriX cursor.getColumnIndex(DownloadManag
er.COLUMN_LOCAL_URI) String
fileName cursor.getString(fileX), fileUri
cursor.getString(uriX) - // TODO Do something with the file.
-
- cursor.close()
-
- registerReceiver(receiver,
- new IntentFilter(DownloadManager.ACTION_DOWNLOAD
_COMPLETE) )
Note The cursor object contains information
about downloaded files
7Listen for Downloaded Files
- ltreceiver androidname".DownloadReceiver"
androidexported"true" androidicon"_at_drawable/d
ownload_icon" gt - ltintent-filtergt
- ltaction androidname"android.intent.action.DOWNLO
AD_COMPLETE"/gt - lt/intent-filtergtlt/receivergt
- IntentFilter f new IntentFilter(DownloadManager.
ACTION_DOWNLOAD_COMPLETE)) - registerReceiver(receiver, f)
- BroadcastReceiver receiver new
BroadcastReceiver() - _at_Override public void onReceive(Context
context, Intent intent) - String id DownloadManager.EXTRA_DOWNLOAD_ID
- long references intent.getLongArrayExtra(id)
// Array of downloaded file ids - for (long reference references)
- String mime getMimeTypeForDownloadedFile
(reference) - // Handle files that this
activity recognizes. -
-
8Specifying Download Locations
Note We cannot download to internal storage
using DownloadManager
- Default Location A shared download cache with
system generated file names - Overriding the download location
requires ltuses-permission androidname - "android.permission.WRITE_EXTERNAL_STORAGE" /gt
- To designate an arbitrary path in external
storage Request request new Request(uri) - request.setDestinationUri(Uri.fromFile(f))
- To designate a standard external download folder
for the application request.setDestinationInExte
rnalFilesDir(this, Environment.DIRECTORY_DOWNL
OADS, fileName) - To designate location which shares with music
players request.setDistinationInExternalPublicDi
r (Environment.DIRECTORY_MUSIC, fileName)
9SD Card File IO
- SD Card path getExternalStorageDirectory()
- How Use standard Java File I/O
- Notes
- Manifest "android.permission.WRITE_EXTERNAL_STORA
GE" - Application manifests can set preference to SD
Card installation - Check SD Availability in Java Environment.getExte
rnalStorageState().equals(Environment.MEDIA_MOUNTE
D) - Convention write to /Android/data/ltpackage_namegt/
files/ - ExampleFile sdCard Environment.getExternalStor
ageDirectory()File dir new File
(sdcard.getAbsolutePath() "
/Android/data/ltpackage_namegt/files/
")dir.mkdirs() // Make directories with
missing parents.File file new File(dir,
"filename") // Instantiate a file object - FileOutputStream f new FileOutputStream(file)
// Open for writing
10Writing to Internal Storage
- Writing to internal storage
- Each application has a designated directory to
which to write files - There is a maximum storage amount, varying per
device - Flags exist to control inter-application access
- MODE_PRIVATE - No access for other applications
- MODE_WORLD_READABLE - Read access for other
applications - MODE_WORLD_WRITABLE - Write access for other
applications - MODE_WORLD_READABLE MODE_WORLD_WRITABLE - Read
/ Write access - Open file for writing new BufferedWriter( new
OutputStreamWriter( openFileOutput(fileName,
MODE_PRIVATE)))
11Onboard File I/O
- Write to a designated place for each application
- Where /data/data/package/files/ using onboard
storage - How Use standard java.io classes, with relative,
not absolute, paths - Applications can
- Write into its application directory
- Create subdirectories
- Give read/write permissions to other applications
(MODE_PRIVATE, MODE_WORLD_READABLE
MODE_WORLD_WRITABLE) - Android Helper classes in the Context object
- getDir(String fileName, int permissions) Creates
or access directory - getFilesDir() Get absolute path to application
directory - getCacheDir() Get non-permanent temporary
storage directory - openFileInput(String fileName) Open a file for
input - openFileOutput(String fileName, int permissions)
Create a new file
Note There is also a sharedPreferences directory
for preferences, a databases directory for SQLite
tables, and a cache directory
12Copy File from Server
- public void DownloadFile(String fileURL, String
fileName) - try HttpURLConnection c
(HttpURLConnection) fileURL.openConnection() c.s
etRequestMethod("GET") c.setDoOutput(false)
// Tru for POST with intent to send to
server c.connect() // Establish connection with
server InputStream in c.getInputStream() //
Ready to read File root Environment.getExterna
lFilesDir(null) // Application area - // Prepare output stream
- FileOutputStream out new FileOutputStream(new
File(root, fileName)) byte buffer new
byte1024 int len 0 // Note the buffer
overflow possibility while ((len
in.read(buffer)) gt 0) out.write(buffer, 0,
len) // Write block - f.close()
- catch (Exception e) Log.d("Downloader",
e.getMessage())
13Preferences
- Initial Purpose
- Persistent storage for user options
- Analogous to cookies in Web-based applications
- More General Purpose
- Maintains the application state across
terminations and restarts - Provides mechanism for applications to
persistently store additional data without using
SQL or Files - Preferences Framework
- Define preferences
- Display settings
- Persist user selections
14Accessing Shared Preference Data
- // getPreferences is a method in the Activity
class - SharedPreferences prefs getPreferences(Activity
.MODE_PRIVATE) - int intValue getInt("IntKey", 22)
- long longValue getLong("LongKey", 0)
- String name getString("NameKey", "")
- int floatValue getFloat("FloatKey", 0.0)
- int booleanValue getBoolean("BoolKey", false)
- SetltStringgt set getStringSet("SetKey", 22)
- MapltString, ?gt keysAndValues getAll()
- Notes
- The second argument is the default if the key was
not previously stored - There is no getDouble() method
15Modifying SharedPreference Data
- SharedPreferences preferences
getPreferences(Activity.MODE_PRIVATE) - // or getSharedPreferences("fileName")
- SharedPreferences.Editor editor
preferences.edit() - editor.putInt("IntKey", 22)
- editor.putLong("LongKey", 0)
- editor.putString("NameKey", "")
- editor.putFloat("FloatKey", 0.0)
- editor.putBoolean("BoolKey", false)
- editor.commit()
- Notes
- puts can be chained editor.putInt(intKey,
22).putLong(LongKey, 0) - editor.apply() works asynchronously,
editor.commit() is synchronous - editor.clear() erases all of the preference keys
- editor.remove(Key) deletes a preference
- Shared preferences use a file name for
multi-activity sharing
16Preference Views
- Preference Screen
- The view on the left
- View on the right appears When the user clicks
after preference screen clicked - Selection Screen
- Modal Dialog on the right
- Saves selection and disappears after a user
selection
17Step 1 Defining Preferences
XML Listing (in res/xml) for the preferences
shown on the previous screen
- lt?xml version"1.0" encoding"utf-8"?gt lt!--
/res/xml/flightoptions.xml --gt - ltPreferenceScreen xmlnsandroid"http//schemas.an
droid.com/apk/res/android" - androidkey"flight_option_preference"
- androidtitle"_at_string/PrefTitle"
androidsummary"_at_string/PrefSummary"gt - ltListPreference androidkey"_at_string/SelectedS
ortOption" - androidtitle"_at_string/ListTitle"
androidsummary"_at_string/ListSummary" - androidentries"_at_array/FlightSortOptions" andro
identryValues"_at_array/FlightSortValues" - androiddialogTitle"_at_string/ChooseFlightOptions
" - androiddefaultValue"_at_string/SortDefaultValue"
/gt - lt/PreferenceScreengt
- public class FlightPreferenceActivity extends
PreferenceActivity - _at_Override protected void onCreate(Bundle
savedInstanceState) - super.onCreate(savedInstanceState)
- addPreferencesFromResource(R.xml.flightoptions)
-
18Define Strings in res/values
- lt?xml version"1.0" encoding"utf-8"?gt
- ltresourcesgt
- ltstring name"AppName"gtPreferences Demolt/stringgt
- ltstring name"PrefTitle"gtMy Preferenceslt/stringgt
- ltstring name"PrefSummary"gtSet Search
Optionslt/stringgt - ltstring name"ListTitle"gtFlight Optionslt/stringgt
- ltstring name"ListSummary"gtSet Search
Optionslt/stringgt - ltstring-array name"FlightSortOptions"gt
- ltitemgtTotal Costlt/itemgtltitemgt of
Stopslt/itemgtltitemgtAirlinelt/itemgtlt/string-arraygt - ltstring-array name"FlightSortValues"gt
- ltitemgt0lt/itemgtltitemgt1lt/itemgtltitemgt2lt/itemgtlt/s
tring-arraygt - ltstring name"ChooseFlightOptions"gtChoose Flight
Optionslt/stringgt - ltstring name"SortDefaultValue"gt1lt/stringgt
- ltstring name"SelectedSortOption"gtSelected Sort
Optionlt/stringgt - ltstring name"menu_prefs_title"gtSettingslt/stringgt
- ltstring name"menu_quit_title"gtQuitlt/stringgt
- lt/resourcesgt
19Manifest
- lt?xml version"1.0" encoding"utf-8"?gt lt!--
AndroidManifest.xml --gt - ltmanifest xmlnsandroid"http//schemas.android.co
m/apk/res/android" - package"com.syh" androidversionCode"1"
androidversionName"1.0"gt - ltapplication androidicon"_at_drawable/icon"
androidlabel"_at_string/AppName"gt - ltactivity androidname".MainActivity"
androidlabel"_at_string/AppName"gt - ltintent-filtergtltaction androidname"android.inte
nt.action.MAIN" /gt - ltcategory androidname"android.intent.category.
LAUNCHER" /gt - lt/intent-filtergt
- lt/activitygt
- ltactivity androidname".PreferenceActivity"
androidlabel"_at_string/PrefTitle"gt - ltintent-filtergtltaction androidname"com.syh.inten
t.action.FlightPreferences" /gt - ltcategory androidname"android.intent.category.P
REFERENCE" /gt - lt/intent-filtergt
- lt/activitygtlt/applicationgtltuses-sdk
androidminSdkVersion"3" /gtlt/manifestgt
20Define Main Android Activity
- // This is MainActivity.java
- _at_Override public void onCreate(Bundle
savedInstanceState) - super.onCreate(savedInstanceState)
- setContentView(R.layout.main) // Assuming
there is a TextView layout - tv (TextView)findViewById(R.id.text1)
- setOptionText() // Helper method to get user
preference and adjust view -
- _at_Override public boolean onCreateOptionsMenu(Menu
menu) - MenuInflater inflater getMenuInflater()
- // Inflate main menu XML with link to preference
option - inflater.inflate(R.menu.mainmenu, menu)
- return true // Menu is now active
21Main Activity Selection Responses
- _at_Override public boolean onOptionsItemSelected
(MenuItem item) - if (item.getItemId() R.id.menu_prefs)
- Intent intent new Intent().setClass(this,
com.syh.PreferenceActivity.class) - this.startActivityForResult(intent, 0) //
Second parameter is for a switch in listener - else if (item.getItemId() R.id.menu_quit)
finish() - return true // Start preference menu when its
item is selected -
- _at_Override public void onActivityResult(int
reqCode, int resCode, Intent data) - super.onActivityResult(reqCode, resCode,
data) - SharedPreferences prefs getDefaultSHaredPrefere
nces(this) - String key this.getResources().getString(R.stri
ng.SelectedSortOption) - String default this.getResources().getString(R.
string.SortDefaultValue) - String selection prefs.getString(key ,
default) - String optionText this.getResources().getStri
ngArray(R.string.FlightSortOptions) - tv.setText("option " pref " ("
optionTextInteger.parseInt(pref) ")") -
22CheckBoxPreference (/res/xml/chkbox.xml)
- lt?xml version"1.0" encoding"utf-8"?gt
- ltPreferenceScreen
- xmlnsandroid "http//schemas.android.com/apk/re
s/android" - androidkey"flight_columns_pref"gt
- ltCheckBoxPreference androidkey"show_airline_col
umn_pref" - androidtitle"Airline" androidsummary"Show
Airline column" /gt - ltCheckBoxPreference androidkey"show_departure_c
olumn_pref" - androidtitle"Departure" androidsummary"Show
Departure column" /gt - ltCheckBoxPreference androidkey"show_arrival_col
umn_pref" - androidtitle"Arrival" androidsummary"Show
Arrival column" /gt - ltCheckBoxPreference androidkey"show_total_trave
l_time_column_pref" - androidtitle"Total Travel Time"androidsummary
"Show Total Travel Time column" /gt - ltCheckBoxPreference androidkey"show_price_colum
n_pref" - androidtitle"Price" androidsummary"Show
Price column" /gt - lt/PreferenceScreengt
Note For Radio Buttons, use the List Preference
23Using Check Box Preferences
- // CheckBoxPreferenceActivity.java
- import android.os.Bundle
- import android.preference.PreferenceActivity
- public class CheckBoxPreferenceActivity extends
PreferenceActivity -
- _at_Override protected void onCreate(Bundle
savedInstanceState) -
- super.onCreate(savedInstanceState)
- addPreferencesFromResource(R.xml.chkbox)
-
-
- // Access preference in an activity
- SharedPreferences prefs getSharedPreferences("fi
leName") - Boolean option prefs.getBoolean("show_price_colu
mn_pref", false)
24EditTextPreference
- lt?xml version"1.0" encoding"utf-8"?gt
- ltPreferenceScreen xmlnsandroidhttp//schemas.and
roid.com/apk/res/android - androidtitleSet Package Name"
- androidsummary"Set the package name for
generated code"gt - ltEditTextPreference androidkey"package_name_pref
erence" - androidtitle"Set Package Name"
androiddialogTitle"Package Name" - androidsummary"Set the package name for
generated code" /gt - lt/PreferenceScreengt
25Categories of Preferences
- lt?xml version"1.0" encoding"utf-8"?gt
- ltPreferenceScreen xmlnsandroid"http//schemas.an
droid.com/apk/res/android" - androidkey"using_categories_in_root_screen"
androidtitle"Categories" - androidsummary"Using Preference Categories"gt
- ltPreferenceCategory xmlnsandroidhttp//schemas.a
ndroid.com/apk/res/android - androidkey"meats_category" androidtitle"Meats
" androidsummary"Meat preferences"gt - ltCheckBoxPreference androidkey"fish_selection_p
ref" androidtitle"Fish" - androidsummary"Fish is great for the healthy"
/gt - ltCheckBoxPreference androidkey"chicken_selectio
n_pref" androidtitle"Chicken" - androidsummary"A common type of poultry" /gt
- ltCheckBoxPreference androidkey"lamb_selection_p
ref" androidtitle"Lamb" - androidsummary"Lamb is a young sheep"
/gtlt/PreferenceCategorygt - ltPreferenceCategory xmlnsandroidhttp//schemas.a
ndroid.com/apk/res/android - androidkey"vegi_category" androidtitle"Vegeta
bles" androidsummary"Vegetable preferences"gt - ltCheckBoxPreference androidkey"tomato_selection
_pref" androidtitle"Tomato " - androidsummary"It's actually a fruit" /gt
- ltCheckBoxPreference androidkey"potato_selection
_pref" androidtitle"Potato" - androidsummary"My favorite vegetable"
/gtlt/PreferenceCategorygtlt/PreferenceScreengt
Purpose Provide titles to groups of similar
preferences
26Preference Fragments
- Android versions 3.0 and newer recommend using
fragments for preferences instead of activities - Check Android Version if (Build.VERSION.SKK_INTlt
Build.VERSION_CODES.HONEYCOME) - / Use preference activity approach /
- else / Use preference fragment approach /
- Implementation Steps
- Define preference screens in XML (as shown on
previous slides) - Add PreferenceFragment to the activity using the
FragmentManager - Create the PreferenceFragment class
27Preference Fragment in an Activity
- public class PreferenceFragmentActivity extends
Activity - _at_Override public void onCreate(Bundle
savedInstanceState) - super.onCreate(savedInstanceState) setConten
tView(R.layout.main) FragmentManager manager
getFragmentManager() FragmentTransaction
trans manager.beginTransaction() MyFragment
fragment new MyFragment() trans.replace(andro
id.R.id.content, fragment1) trans.addToBackSta
ck(null) - trans.commit()
Note Dont add the fragment to the application
manifest
28Preference Fragment Class
- public class MyFragment extends
PreferenceFragment - _at_Override public void onCreate(Bundle
savedInstanceState) - super.onCreate(savedInstanceState) //
configure the preferences from an XML
file addPreferencesFromResource(R.xml.preferenc
es) - PreferenceManager manager getPreferenceManager
() - SharedPreferences shared manager.getSharedPref
erences() - shared.registerOnSharedPreferenceChangeListener(
this) -
- onSharedPreferenceChanged(SharedPreferences
pref, String key) - / Handle preference change here /
29SQLite
- Android SQLite
- Lightweight database system
- Standards-compliant
- Single Tier (no server involved)
- Open source
- Standard SQL queries
- SELECT ID, CITY, STATE FROM STATION WHERE LAT_N
gt 39.7 - Application content providers provide
- Standard interface to SQL tables to other
applications - For application private tables, content providers
are not needed
30Authorities / URI
Purpose Publish application SQL tables and data
for external activity access
- Definition An authority is a registered name on
an android device (ex Content Provider name.
Authorities must be unique on the system. - Registration Accomplished in an application
manifest - Syntax com.company.someProvider
- Examples org.acorns.LessonProvider or
com.android.provider.BookProvider - Analogy Authorities are to Android what domain
names are to the Web - Activity access using URIs
- Syntax content//authority-name/path-segment/path
-segment/ - Access book table content//com.anroidbook.provid
er.BookProvider/books - Access particular book content//com.anroidbook.
provider.BookProvider/books/12 - Activity Interface Through Content Providers,
which fill in abstract methods
31MIME Types
Purpose Indicate a type of data that a content
provider deals with
- Definition Industry standard way to indicate a
particular data format - Syntax Two text strings separated by slashes
- First part (category) application, audio, image,
text, video, etc - Second Part (codec) html, css, xml, pdf, rtf,
etc. - Examples text/htm, application/pdf, image/jpeg,
audio/mpeg, etc. - Android MIME Types (Similar syntax as standard
MIME types) - First Part
- Access single items vnd.android.cursor.item
- Access multiple items vnd.android.cursor.dir
- Second Part vnd.yourCompany.type
- Examples vnd.android.cursor.dir/vnd.google.note
or vnd.android.cursor.item/vnd.google.note
32Content Provider Framework
- An application registers its content provider
using its manifest - The code for the content provider is written
- An application request data using the published
MIME type - Android looks through the registered manifests
for an appropriate provider - The provider activity is launched to manipulate
or return data - If more than one provider exists, the user
selects the one to use - Android Built-in content providers
content//media/internal/images/,
content//media/external/images/,content//contac
ts/people/
33Content Providers
A well-defined interface to data for application
access
- Register an authority using the
AndroidManifest.xml file - Definition An authority is a registered name
- Analogy authority is to an Android device what a
domain name is to the Internet - Syntax ( Using a package name minimizes
redundancies on a system)ltprovider
androidname"fooProvider" androidauthorities"c
om.company.fooProvider" /gt - Exampleltprovider androidname"
com.acorns.LessonProvider" androidauthorities"c
om.acorns.provider.Lessons" /gt - Another application exampleltprovider
androidnameNotePadProvider" androidauthoritie
s"com.google.provider.NotePad" /gt
34MIME Types
Acronym Multipurpose Internet Mail Extension
- Specifies the data type an activity can process
- Register in the AndroidManifest.xml file
- Example "vnd.android.cursor.dir/vnd.google.note"
OR "vnd.android.cursor.dir/vnd.google.note" - Sample Syntax vnd.android.cursor.ltoptgt/vnd.compan
y.type - vnd non-standard type (vendor)
- android.cursor required for ContentProvider
Manifest registrations - item single record dir collection of records
- google company name
- note content type
- AndroidManifest.xml registrationltdata
androidmimeType"vnd.android.cursor.dir/vnd.googl
e.note" /gt
35Referring to a Content Provider
- URI and URL
- Uniform resource identifier (URI) Identifies the
name of a resource, but not necessarily
information regarding its location - Uniform resource Location (URL) is a URI, that
identifies a resource location - Text usage URI for content providers URL for
referring to Web locations - URI of a content providercontent//com.company.fo
oProvider/content//com.acorns.provider.Lessons/
content//com.google.provider.NotePad/ - Note Android provided shortcut URIs contacts
for com.google.android.contacts - Refer to the notes SQLite notes
tablecontent//com.google.provider.NotePad/notes/
- Refer to the tenth notecontent//com.google.provi
der/NotePad/notes/10
36Register the Provider
- ltprovider androidname"NotePadProvider"
- androidauthorities"com.google.provid
er.NotePad" /gt - ltactivity androidname"NotesList"
androidlabel"_at_string/title_notes_list"gt - ltintent-filtergt
- ltaction androidname"android.intent.action
.MAIN" /gt - ltcategory androidname"android.intent.cate
gory.LAUNCHER" /gt - lt/intent-filtergt
- ltintent-filtergt
- ltaction androidname"android.intent.action.VIEW"
/gt - ltdata androidmimeType"vnd.android.cursor.dir/vn
d.google.note" /gt - lt/intent-filtergtltintent-filtergt
- ltaction androidname"android.intent.action.
GET_CONTENT" /gt - ltdata androidmimeType"vnd.android.cursor.item/v
nd.google.note" gt - lt/intent-filtergt lt/activitygt
37Implement the Provider
- Plan the Database
- Extend the ContentProvider class
- Fill in a variety of methods to be overloaded
- onCreate() When the database is created
- onUpgrade() When the database layout changes
- getType() Return the MIME data type
- query() Handle queries to the data
- insert(), delete(), and update() methods
38A Managed Query in Content Provider
Purpose Query SQL lite table for relevant data
(cursor) Definition An Android cursor is a
collection of rows of data
- // Specify needed columns
- string projection new string
- People._ID, People.NAME, People.NUMBER
- // Specivy URI using static Contacts constants
- Uri contactUri Contacts.People.CONTENT_URI
- // Invoke a content provider query
- Cursor cursor managedQuery( contactUri,
- projection, //Which columns to return
- null, null, // Selection and WHERE clauses
- Contacts.People.NAME " ASC") // Order-by
(ascending by name)
Other classes exist (ex SQLiteQueryBuilder) for
using standard SQL strings
39Navigating through a cursor
- Manipulate the cursor (row pointer)
- if (cursor.moveToFirst() false)
- cursor.isBeforeFirst(), cursor.isAfterLast,
cursor.isClosed() - while (cursor.moveToNext()) / code here /
- for (cursor.moveToFirst() !cursor.isAfterLast()
cur.moveToNext) - Get column numbers from names
- int nameColumn cursor.getColumnIndex(People.NAME
) - int phoneColumn cursor.getColumnIndex(People.NUM
BER) - Get Data from column
- String name cursor.getString(nameColumn)
- String number cursor.getString(phoneColumn)
- Prerequisites Know column names, data types,
column name to index relationship
After a query, the cursor points before the
first use moveToFirst() use moveToNext() for
the initial access
40Other Access Methods
- Add
- ContentValues values new ContentValues()
- values.put(BookProviderMetaData.BookTableMetaData.
BOOK_NAME, "book1") - values.put(BookProviderMetaData.BookTableMetaData.
BOOK_AUTHOR, "author-1") - ContentResolver resolve context.getContentResolv
er() - URI uri BookProviderMetaData.BookTableMetaData.C
ONTENT.URI - Uri inserted resolve.insert(uri, values)
- Delete Assume that bookIndex is pre-defined
- ContentResolver resolve context.getContentResolv
er() - URI uri BookProviderMetaData.BookTableMetaData.C
ONTENT.URI - Uri delURI uri.withAppendedPath(uri,
Integer.toString(bookIndex)) - resolve.delete(delUri, null, null)
- Count
- URI uri BookProviderMetaData.BookTableMetaData.C
ONTENT.URI - Cursor cursor activity.managedQuery(uri, null,
null, null, null) - Int numberOfRecords cursor.getCount()
- cursor.close()
See next slide for symbolic constant definitions
41BookProviderMetaData class
42Content Resolver
- Definition A Content Resolver is an Android
class that matches the URI to an available
Content Provider that can handle the data - Purpose Separate the provider from the data,
enabling the possibility of multiple content
providers available to handle the same data types - Access Use a collection of method calls to
insert, retrieve, delete, update data records - Example (insert a new note into the notepad)
- ContentResolver resolver activity.getContentRe
solver() - Uri newUri resolver.insert(Notepad.Notes.CONTE
NT_URI, values)
Note values is an instance of the ContentValues
class
43Store Files in an SQLite Database
- Steps
- Create the SQLite table with a column called
_data - Get a writeable output stream
- Write the file to the reserved column name _data
- I/O Example
- //Use a content resolver to insert the record
- ContentResolver contentResolver
activity.getContentResolver() - Uri newUri contentResolver.insert(Notepad.Notes
.CONTENT_URI, values) - //Use the content resolver to get an output
stream directly - OutputStream outStream contentResolver().openOu
tputStream(newUri) - writeFileToRecord(outStream)
- outStream.close()
ContentValues object contains a set of values
Note The data is actually stored in a separate
area. The _data column contains the Uri reference
to the data
44DataBase Helper
- Purpose
- Manage database creation and version management
- Responsible for creating and upgrading an SQLite
database - Defer database opening and updating to the first
use - Avoids blocking application startup with compute
intensive database upgrades - Contains methods for getWritableDatabase and
getReadableDatabase - Simplifies Content Provider access to files
- Eliminates concern over whether the application
was terminated - Implementation
- Create a class that extends SQLiteOpenHelper
- Override onCreate() and onUpgrade() methods
45Helper Code (Two tables Employees, Department)
This application will need to override onCreate
and onUpgrade
- public class DatabaseHelper extends
SQLiteOpenHelper - static final String dbName"demoDB"
- static final String employees"Employees"
- static final String colID"EmployeeID"
- static final String colName"EmployeeName"
- static final String colAge"Age"
- static final String colDept"Dept"
- static final String depts"Department"
- static final String colDeptID"DeptID"
- static final String colDeptName"DeptName"
- static final String viewEmps"ViewEmps"
- public DatabaseHelper(Context context) //
Constructor - super(context, dbName, null / Extension to
cursor class /, 1 / version /)
46Helper Example (cont.)
- public void onCreate(SQLiteDatabase db)
- db.execSQL("CREATE TABLE " "depts
- " (" colDeptID " INTEGER PRIMARY KEY , "
colDeptName " TEXT)") - db.execSQL("CREATE TABLE "employees
- " (" colID " INTEGER PRIMARY KEY
AUTOINCREMENT, " - colName " TEXT, " colAge " Integer, "
- colDept " INTEGER NOT NULL , FOREIGN KEY ("
colDept ") REFERENCES " - deptTable" ("colDeptID"))")
- db.execSQL("CREATE TRIGGER fk_empdept_deptid ""
BEFORE INSERT ON "employees - " FOR EACH ROW BEGIN" " SELECT CASE WHEN (
(SELECT " colDeptID " FROM " depts "
WHERE " colDeptID "new." colDept " ) IS
NULL)" - " THEN RAISE (ABORT, 'Foreign Key Violation')
END" " END") - db.execSQL("CREATE VIEW " viewEmps " AS
SELECT " employees "." colID - " AS _id," " " employees "." colName
", " employees "." colAge ", " - depts "." colDeptName "" " FROM "
employees " JOIN " depts - " ON " employees "." colDept" " depts
"." colDeptID )
47Helper Example (cont.) Upgrading the DB
Called when the version number in the constructor
changes
- public void onUpgrade(SQLiteDatabase db, int
oldVersion, int newVersion) -
- db.execSQL("DROP TABLE IF EXISTS "employees)
- db.execSQL("DROP TABLE IF EXISTS "depts)
- db.execSQL("DROP TRIGGER IF EXISTS
dept_id_trigger") - db.execSQL("DROP TRIGGER IF EXISTS
dept_id_trigger22") - db.execSQL("DROP TRIGGER IF EXISTS
fk_empdept_deptid") - db.execSQL("DROP VIEW IF EXISTS "viewEmps)
- onCreate(db)
Note The activity can respond to menu selections
to insert, remove, and update rows
48Using a Database helper
- Helper new Helper(context, Helper.DATABASE_NAME,
null, Helper.VERSION) - SQLiteDatabase db Helper.getWritableDatabase()
// Perform query - Cursor cursor db.query(Helper.DATABASE_TABLE,
result_columns, where, - whereArgs, group, having, order)
- SQLiteDatabase db hoardDBOpenHelper.getWritableD
atabase() // Insert row - db.insert(HoardDBOpenHelper.DATABASE_TABLE, null,
newValues) - SQLiteDatabase db hoardDBOpenHelper.getWritableD
atabase() // Update row - db.update(Helper.DATABASE_TABLE, newValues,
where, whereArgs) - SQLiteDatabase db Helper.getWritableDatabase()
// Delete matching rows - db.delete(Helper.DATABASE_TABLE, where,
whereArgs)
49Find media titles and albums
Using the Android provided Media Store Content
Provider
- String projection MediaStore.Audio.AudioC
olumns.ALBUM, - MediaStore.Audio.AudioColumns.TITLE
-
- Uri uri MediaStore.Audio.Media.EXTERNAL_CONTENT_
URI - Cursor cursor getContentResolver().query(uri,
projection, null, null, null) - int aX cursor.getColumnIndexOrThrow(MediaStore.Au
dio.AudioColumns.ALBUM) - int tX cursor.getColumnIndexOrThrow(MediaStore.
Audio.AudioColumns.TITLE) - String result new Stringcursor.getCount()
- while (cursor.moveToNext())
- String title cursor.getString(tX), album
cursor.getString(aX) - resultcursor.getPosition() title " ("
album ")" -
- cursor.close()
Other Native Content Providers Browser,
Contacts, Calendar, Call log
50Accessing the Contact List
- int iDCol ContactsContract.Contacts.ID
- int nameCol ContactsContract.Contacts.DISPLAY_N
AME - Uri uri ContactsContract.Contacts.CONTENT_URI
- String projection iDCol, nameCol
- Cursor cursor getContentResolver().query(uri,
projection, null, null, null) - int nameIndex cursor.getColumnIndexOrThrow(name
Col) - int idIndex cursor.getColumnIndexOrThrow(contact
IDCol) - String result new Stringcursor.getCount()
- while(cursor.moveToNext())
- String name cursor.getString(nameIndex)
- String id cursor.getString(idIndex)
- resultcursor.getPosition() name " (" id
")" -
- cursor.close()
51Searchable Applications
- Expose the application to search-facilities
- Alternatives
- Search bar appearing after hardware search button
pressed or a program method call - Search widget placed somewhere on the application
view - Quick Search implemented as a home screen widget
- Enable voice search
52Making an Application Searchable
- In the manifest
- Create an xml file with the searchable attributes
- Mark the application searchable
- Define the intent for the activity processing
searches - Either create a search activity
- Launch when the search is initiated
- Process and display the results
- Or use a search dialog (floating search box)
- Hidden by default, but can be initiated by
calling onSearchRequested(), which is an Activity
class method - If the device doesnt contain a dedicated search
button, add a menu option or widget that calls
onSearchRequested() - Or use the SearchView widget
53Searchable configuration in res/xml
Filename searchable.xml
- lt?xml version"1.0" encoding"utf-8"?gtltsearchable
xmlnsandroidhttp//schemas.android.com/apk/res
/android - androidlabel"_at_string/app_label
- androidhint"_at_string/search_hint"
gtlt/searchablegt - The label (required attribute) becomes visible
when search is launched and contains the search
application name - The hint appears in the search box before users
enter a query. It contains a hint about what can
be searched - Other attributes are for search suggestions and
voice search
54Searchable launch in Manifest
- ltapplicationgt
- ltactivity androidname".SearchActivity
- androidlaunchMode"singleTop"gt
- ltintent-filtergt
- ltaction androidname"android.intent.action.SEAR
CH" /gt - lt/intent-filtergt
- ltmeta-data androidname"android.app.searchable"
- androidresource"_at_xml/searchable" /gt
- lt/activitygt
- Note singleTop prevents multiple instances on
the back stack
55Search Activity
- _at_Overridepublic void onCreate(Bundle
savedInstanceState) - super.onCreate(savedInstanceState)
- setContentView(R.layout.search)
- // Get the intent, verify the action and get the
query - Intent intent getIntent()
- if (Intent.ACTION_SEARCH.equals(intent.getAction(
))) - String query intent.getStringExtra(SearchMana
ger.QUERY) - doMySearch(query) // Process the request
-
56Services
- Services handle background events separate from a
View - Services reside in the main thread of the
launching process! Use thread facilities to
avoid blocking the main thread - Only foreground activities have a higher
priority, so services are rarely terminated by
the OS - Proper Handling
- Main activity makes requests
- Service starts worker threads to perform requests
- Bind services to the main thread
- Callbacks to the main activity completes
transactions.
57Creating a Service
- Create a class extending the service class
- Register the service in the application
manifest ltservice androidenabled"true" - androidname".MyService" androidpermission"fo
o.bar.MY_SERVICE_PERMISSION"/gt - Start and stop of the service in the main
activity - Use separate threads for compute-bound operations
- Bind service to the main activities so the main
activity can call service class methods and then
update its user interface views - Self-terminate the service when it is no longer
needed
58Starting and Stopping Services
- Explicitly start My Service
- Intent intent new Intent(this,
MyService.class) - // TODO Add extras if required.
- startService(intent)
- Implicitly start a music Service
- Intent intent new Intent(MyMusicService.PLAY_AL
BUM) intent.putExtra(MyMusicService.ALBUM_NAM
E_EXTRA, "United") - intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA,
"Pheonix") - startService(intent)
- Explicitly stop My Service
- stopService(new Intent(this, MyService.class))
- Implicitly stop Music Service
- stopService(new Intent(MyMusicService.PLAY_ALBUM)
)
59Creating a Bindable Service
Purpose Enable activities to access service
methods
- public class LocalService extends Service
- private final IBinder binder new
LocalBinder() public class LocalBinder extends
Binder - LocalService getService() return
LocalService.this - _at_Override public void onCreate()
- // TODO Actions to perform when service is
created. - // Return instance after activity call
onBindService() call _at_Override public
IBinder onBind(Intent intent) - return binder
- / method for clients / public int
getRandomNumber() return Math.random()
60- public class BindingActivity extends Activity
// An Activity using a Bound Service - LocalService service boolean bound
false private ServiceConnection connection
new ServiceConnection() // Anonymous instance - _at_Override public void onServiceConnected(Compon
entName name, IBinder service) - LocalBinder binder(LocalBinder) service
- servicebinder.getService() boundtrue
_at_Override public void onServiceDisconnected
(ComponentName arg0) - bound false
- _at_Override protected void
onCreate(Bundle savedInstanceState) - super.onCreate(savedInstanceState)
setContentView(R.layout.main) _at_Override
protected void onStart() - super.onStart() Intent intent new
Intent(this, LocalService.class)
bindService(intent, connection,
Context.BIND_AUTO_CREATE) _at_Override
protected void onStop() - super.onStop() if (bound)
unbindService(connection) bound false
public void onButtonClick(View v) - if (bound) int num service.getRandomNum
ber() - Toast.makeText(this, "number "num,
Toast.LENGTH_SHORT).show()
61Updating the GUI from a Service
- The activity registers a listening object with
the service - The service broadcasts an intent and the activity
responds - Use a Handler in an activity
- private final Handler handler new Handler() //
Process queue of runnables - private OnClickListener buttonListener new
OnClickListener() - public void onClick(View v)
- new Thread(new Runnable()
- pubic void run() //.... hard work in
separate thread - handler.post(new Runnable() public
void run() // ... update the UI ) - ).start()
-
-
-
Note There are other possibilities. For example,
one could use AsyncTask, IntentService or Loader
objects