Download presentation
Presentation is loading. Please wait.
1
The 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
2
Managing 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
3
Application 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. However file names in res/raw are compiled into resource access codes. Files stored directly in the application are read-only and cannot be modified Access using standard Java I/O operations InputStream is = app.getResources().openRawResource(R.raw.foo); in = BufferedReader in = new BufferedReader(new InputStreamReader(is))); InputStream rawRes = context.getAssets().open("foo.bar"); Reader r = new BufferedReader(new InputStreamReader(rawRes, "UTF8")); Note: Use lower case alphanumeric to name files in res/raw
4
Downloading 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
5
Reading from the Web public byte[] getUrlBytes(String urlSpec) throws IOException { URL url = new URL(urlSpec); HttpURLConnection c = (HttpURLConnection)url.openConnection(); try{ ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStream in = connection.getInputStream(); if (c.getResponseCode()!=HttpURLConnection.HTTP_OK) { throw new IOException(c.getResponseMessage() + ": with " + urlSpec); } int bytesRead; byte[] buffer = new byte[1024]; while ((bytesRead = in.read(buffer)) > 0) { out.write(buffer, 0, bytesRead); } out.close(); return out.toByteArray(); } finally { c.disconnect(); } }
6
Parsing JSON Get id for the first item in the array (where index == 0)
JavaScript Object Notation: common format for transferring Internet data { "photos"; { "page": 1, "pages": 10, "perpage":100, "total": 1000, "photo": [ { "id": " ", "owner": “secret:“, "d6d20af93e", "server": "7365", "farm": "8", "title": "Low and Wisoff at Work", "ispublic": "1", "isfriend": "0", "isfamily": "0" }, … ] }, "stat": "ok“ } Get id for the first item in the array (where index == 0) getJSONObject("photos") getJSONArray("photo") getJSONObject(index) getString("id")
7
Background Tasks Problem: Android terminates applications that do computationally intense operations on the user interface threads First solution: Create a background thread that does the operations and notifies the User thread when done Disadvantage: requires low level code to implement Second solution: Extend Async task that runs in a background thread and handles the details of notifying the user level thread Disadvantage: The user interface is notified once after completion. Third solution: Use a download handler that notifies the user level thread multiple times. For example, when each image of a list is downloaded Disadvantage: More flexibility comes at the cost of more complexity Fourth solution: Use the API for one of the download applications Disadvantage: Less flexible
8
Async Task to download a File
<type of input, type of progress, type of results> class Fetch extends AsyncTask<Void,Void,List<GalleryItem>> { @Override List<GalleryItem> doInBackground(Void... params) { return new FlickrFetchr().fetchItems(); } @Override void onPostExecute(List<GalleryItem> items) { mItems = items; setupAdapter(); }
9
Download Using Download Manager
DownloadManager m= (DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE); long myReference = m.enqueue(new Request(Uri.parse(R.string.webLoc))); BroadcastReceiver receiver = new BroadcastReceiver() public void onReceive(Context context, Intent intent) { long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (myReference == reference) { Query query = new Query(); Cursor cursor = downloadManager.query(query.setFilterById(reference)); if (cursor.moveToFirst()) { int fileX = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME); int uriX = cursor.getColumnIndex(DownloadManager.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
10
Listen for Downloaded Files
<receiver android:name=".DownloadReceiver" android:exported="true" // Receive from other applications > // For notification <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/> </intent-filter></receiver> IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); registerReceiver(receiver, new BroadcastReceiver() 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. } } } }, filter);
11
Specifying Download Locations
Note: We cannot download to internal storage using DownloadManager External Storage: Removable storage (ex: SD card) or internal non-removable storage. External storage files are public and user modifiable via USB storage Download cache: Shared download cache with system generated name Overriding the download location requires: <uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> To designate an arbitrary path in external storage Request request = new Request(uri); // DownloadManager.Request request.setDestinationUri(Uri.fromFile(f)); To designate a standard external download folder for the application request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, fileName); To designate location which shares with music players request.setDistinationInExternalPublicDir (Environment.DIRECTORY_MUSIC, fileName);
12
SD Card File IO SD Card path: getExternalStorageDirectory()
How: Use standard Java File I/O Notes: Manifest: "android.permission.WRITE_EXTERNAL_STORAGE" Application manifests can set preference to SD Card installation Check SD Availability in Java: Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) Convention: write to /Android/data/<package_name>/files/ Example File sdCard = Environment.getExternalStorageDirectory(); File dir = new File (sdcard.getAbsolutePath() + " /Android/data/<package_name>/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
13
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)));
14
Onboard 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
15
Copy File from Server public void DownloadFile(String fileURL, String fileName) { try { HttpURLConnection c = (HttpURLConnection) fileURL.openConnection(); c.setRequestMethod("GET"); c.setDoOutput(false); // True for “POST” with intent to send to server c.connect(); // Establish connection with server InputStream in = c.getInputStream(); // Ready to read File root = Environment.getExternalFilesDir(null); // Application area // Prepare output stream FileOutputStream out = new FileOutputStream(new File(root, fileName)); byte[] buffer = new byte[1024]; int len = 0; // Note the buffer overflow possibility while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } // Write block f.close(); } } } catch (Exception e) { Log.d("Downloader", e.getMessage()); }
16
Preferences Initial Purpose: More General 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
17
Accessing Shared Preference Data
// getPreferences is a method in the Activity class SharedPreferences prefs = getPreferences(Activity.MODE_PRIVATE); int intValue = prefs.getInt("IntKey", 22); long longValue = prefs.getLong("LongKey", 0); String name = prefs.getString("NameKey", ""); int floatValue = prefs.getFloat("FloatKey", 0.0); int booleanValue = prefs.getBoolean("BoolKey", false); Set<String> set = prefs.getStringSet("SetKey", 22); Map<String, ?> prefs.keysAndValues = getAll(); Note: The second argument is the default if the key was not previously stored
18
Modifying 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
19
Preference Views Preference Screen Selection Screen
The view on the left View on the right appears When the user touches after preference screen Selection Screen Modal Dialog on the right Saves selection and disappears after a user selection
20
Step 1: Defining Preferences
XML Listing (in res/xml) for the preferences shown on the previous screen <?xml version="1.0" encoding="utf-8"?> <!-- /res/xml/flightoptions.xml --> <PreferenceScreen xmlns:android=" android:key="FlightOptionPreference" > <ListPreference /> </PreferenceScreen> public class FlightPreferences extends PreferenceActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.flightoptions); } }
21
Define Strings in res/values
<?xml version="1.0" encoding="utf-8"?> <resources> <string name=“FlightOptionPreference">Preferences Demo</string> <string name="PrefTitle">My Preferences</string> <string name="ListTitle">Flight Options</string> <string name="ListSummary">Set Search Options</string> <string-array name="FlightSortOptions"> <item>Total Cost</item><item># of Stops</item><item>Airline</item> </string-array> <string-array name="FlightSortValues"> <item>0</item><item>1</item><item>2</item> <string name="ChooseFlightOptions">Choose Flight Options</string> <string name="SortDefaultValue">1</string> <string name="SelectedSortOption">Selected_Sort_Option</string> </resources>
22
Manifest <?xml version="1.0" encoding="utf-8"?> <!-- AndroidManifest.xml --> <manifest xmlns:android=" package="com.syh" android:versionCode="1" android:versionName="1.0"> <application <activity android:name=".MainActivity" <intent-filter><action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".FlightPreferences" <intent-filter><action android:name=".FlightPreferences" /> <category android:name="android.intent.category.PREFERENCE" /> </activity></application><uses-sdk android:minSdkVersion=“15" /></manifest>
23
Define Main Android Activity
// This is 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 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
24
Main Activity Selection Responses
@Override public boolean onOptionsItemSelected (MenuItem item) { if (item.getItemId() == R.id.menu_prefs) { Intent intent = new Intent().setClass(this, .FlightPreferences.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 public void onActivityResult(int reqCode, int resCode, Intent data) { super.onActivityResult(reqCode, resCode, data); SharedPreferences prefs = getDefaultSHaredPreferences(this); String key = this.getResources().getString(R.string.SelectedSortOption); String default = this.getResources().getString(R.string.SortDefaultValue); String pref = prefs.getString(key , default); String[] optionText = this.getResources().getStringArray(R.string.FlightSortOptions); tv.setText("option = " + pref + " (" + optionText[Integer.parseInt(pref)] + ")");
25
CheckBoxPreference (/res/xml/chkbox.xml)
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android =" android:key="flight_columns_pref"> <CheckBoxPreference android:key="show_airline_column_pref" android:title="Airline" android:summary="Show Airline column" /> <CheckBoxPreference android:key="show_departure_column_pref" android:title="Departure" android:summary="Show Departure column" /> <CheckBoxPreference android:key="show_arrival_column_pref" android:title="Arrival" android:summary="Show Arrival column" /> <CheckBoxPreference android:key="show_total_travel_time_column_pref" android:title="Total Travel Time“ android:summary="Show Total Travel Time column“ /> <CheckBoxPreference android:key="show_price_column_pref" android:title="Price" android:summary="Show Price column" /> </PreferenceScreen> Note: For Radio Buttons, use the List Preference
26
Using Check Box Preferences
// CheckBoxPreferenceActivity.java import android.os.Bundle; import android.preference.PreferenceActivity; public class CheckBoxPreferenceActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.chkbox); } // Access preference in an activity SharedPreferences prefs = getSharedPreferences("fileName"); Boolean option = prefs.getBoolean("show_price_column_pref", false);
27
EditTextPreference <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android= android:title=“Set Package Name" android:summary="Set the package name for generated code"> <EditTextPreference android:key="package_name_preference" android:title="Set Package Name" android:dialogTitle="Package Name" android:summary="Set the package name for generated code" /> </PreferenceScreen>
28
Categories of Preferences
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android=" android:key="using_categories_in_root_screen" android:title="Categories" android:summary="Using Preference Categories"> <PreferenceCategory android:key="meats_category" android:title="Meats" android:summary="Meat preferences"> <CheckBoxPreference android:key="fish_selection_pref" android:title="Fish" android:summary="Fish is great for the healthy" /> <CheckBoxPreference android:key="chicken_selection_pref" android:title="Chicken" android:summary="A common type of poultry" /> <CheckBoxPreference android:key="lamb_selection_pref" android:title="Lamb" android:summary="Lamb is a young sheep" /></PreferenceCategory> android:key="vegi_category" android:title="Vegetables" android:summary="Vegetable preferences"> <CheckBoxPreference android:key="tomato_selection_pref" android:title="Tomato " android:summary="It's actually a fruit" /> <CheckBoxPreference android:key="potato_selection_pref" android:title="Potato" android:summary="My favorite vegetable" /></PreferenceCategory></PreferenceScreen> Purpose: Provide titles to groups of similar preferences
29
Preference Fragments Android versions 3.0 and newer recommend using fragments for preferences instead of activities Check Android Version: if (Build.VERSION.SKK_INT<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
30
Preference Fragment in an Activity
public class PreferenceFragmentActivity extends Activity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); FragmentManager manager = getFragmentManager(); FragmentTransaction trans = manager.beginTransaction(); MyFragment fragment = new MyFragment(); trans.replace(android.R.id.content, fragment1); trans.addToBackStack(null); trans.commit(); } } Note: Don’t add the fragment to the application manifest
31
Preference Fragment Class
public class MyFragment extends PreferenceFragment public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // configure the preferences from an XML file addPreferencesFromResource(R.xml.preferences); PreferenceManager manager = getPreferenceManager(); SharedPreferences shared = manager.getSharedPreferences(); shared.registerOnSharedPreferenceChangeListener(this); } onSharedPreferenceChanged(SharedPreferences pref, String key) { /* Handle preference change here */ }
32
SQLite Android SQLite: Standard SQL queries
Lightweight database system Standards-compliant Single Tier (no server involved) Open source Standard SQL queries “SELECT ID, CITY, STATE FROM STATION WHERE LAT_N > 39.7;” Application content providers provide Standard interface to SQL tables to other applications For application private tables, content providers are not needed
33
Steps to work with a database
Define the Schema for an SQL table Use database helper to automatically create or update the table Create a Cursor wrapper and add methods to return appropriate objects Implement appropriate add, update, remove, and get methods in the model
34
Define Schema public class CrimeDbSchema { public final static class CrimeTable { public static final String NAME = "crimes"; public static final class Cols { public static final String UUID = "uuid"; public static final String TITLE = "title"; public static final String DATE = "date"; public static final String SOLVED = "solved"; } } }
35
DataBase Helper Purpose Implementation
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
36
DatabaseHelper public class CrimeBaseHelper extends SQLiteOpenHelper { private static final int VERSION = 1; private static final String DATABASE_NAME = "crimeBase.db"; public CrimeBaseHelper(Context context) { super(context, DATABASE_NAME, null, VERSION); public void onUpgrade (SQLiteDatabase sqLiteDatabase, int i, int i1) public void onCreate(SQLiteDatabase db) { db.execSQL("create table " + CrimeTable.NAME + "(" + "_id integer primary key autoincrement, " + CrimeTable.Cols.UUID + ", " + CrimeTable.Cols.TITLE + ", " + CrimeDbSchema.CrimeTable.Cols.DATE + ", " + CrimeTable.Cols.SOLVED + ")" ); } } To access: mDatabase = new CrimeBaseHelper (context.getApplicationContext()).getWritableDatabase();
37
Helper 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); } SQLite View: Pre-packaged select statement SQLite Trigger: Callback methods invoked to respond to database events Note: The activity can respond to menu selections to insert, remove, and update rows
38
CursorWrapper public class CrimeCursorWrapper extends CursorWrapper { public CrimeCursorWrapper(Cursor cursor) { super(cursor); } public Crime getCrime() { String uuidString = getString(getColumnIndex(CrimeTable.Cols.UUID)); String title = getString(getColumnIndex(CrimeTable.Cols.TITLE)); long date = getLong(getColumnIndex(CrimeTable.Cols.DATE)); int isSolved = getInt(getColumnIndex(CrimeTable.Cols.SOLVED)); Crime crime = new Crime(UUID.fromString(uuidString)); crime.setTitle(title); crime.setDate(new Date(date)); crime.setSolved(isSolved !=0 ); return crime; } }
39
Useful Supporting Methods
private static ContentValues getContentValues(Crime crime) { ContentValues values = new ContentValues(); values.put(CrimeTable.Cols.UUID, crime.getId().toString()); values.put(CrimeTable.Cols.TITLE, crime.getTitle()); values.put(CrimeTable.Cols.DATE, crime.getDate().getTime()); values.put(CrimeTable.Cols.SOLVED, crime.isSolved()?1:0); return values; } private CrimeCursorWrapper queryCrimes (String whereClause, String[] whereArgs) { Cursor cursor = mDatabase.query( CrimeTable.NAME, null, // Columns; null selects all whereClause, whereArgs, null, /* Group by */ null, /* having */ null /* orderBy */ ); return new CrimeCursorWrapper(cursor); }
40
Altering the Database public void addCrime(Crime c) { ContentValues values = getContentValues(c); mDatabase.insert(CrimeTable.NAME, null, values); } public void updateCrime(Crime c) { String uuidString = c.getId().toString(); ContentValues values = getContentValues(c); mDatabase.update(CrimeTable.NAME, values, CrimeTable.Cols.UUID + " = ?", new String[] { uuidString }); } public void deleteCrime(Crime c) { String uuidString = c.getId().toString(); mDatabase.delete(CrimeTable.NAME, CrimeTable.Cols.UUID + " = ?", new String[] { uuidString }); }
41
Retrieving from the Database
public Crime getCrime(UUID id) { CrimeCursorWrapper cursor = queryCrimes(CrimeTable.Cols.UUID + " = ?", new String[] {id.toString() } ); try { if (cursor.getCount() == 0) { return null; } cursor.moveToFirst(); return cursor.getCrime(); } finally { cursor.close() } } public List<Crime> getCrimes() { List<Crime> crimes = new ArrayList<>(); CrimeCursorWrapper cursor = queryCrimes(null, null); try { cursor.moveToFirst(); while (!cursor.isAfterLast()) { crimes.add(cursor.getCrime()); cursor.moveToNext(); } } finally { cursor.close() } return crimes; }
42
Store 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().openOutputStream(newUri); writeFileToRecord(outStream); outStream.close(); ContentValues object contains a set of key/values Note: The data is actually stored in a separate area. The _data column contains the Uri reference to the data
43
Content Provider Framework
An application registers its content provider using its manifest The code for the content provider is written An application requests a provider using the published MIME type Android looks through the registered manifests for a match The provider activity is launched to manipulate or return data If more than one provider exists, the user selects the one to use Query a content provider using the provider’s URI: content://media/internal/images/, content://media/external/images/, content://contacts/people/
44
Creating a 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 Register the provider in the manifest
45
Authorities Purpose: Publish application SQL tables and data for external activity access Definition: An authority is a system unique manifest registered name. Authorities are to Android what domain names are to the web Example: <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> </provider> Activity content provider access with a Uniform Resource Identifier (URI) Syntax: content://<relative path> Access book table: content://com.anroidbook.provider.BookProvider/books Access particular book: content://com.anroidbook.provider.BookProvider/books/12 grantURIPermission: All data accessible (true) or subsets of data accessible exported: Accessible by other applications
46
MIME 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
47
MIME 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.<opt>/vnd.company.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 registration <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
48
Referring 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 provider content://com.company.fooProvider/ 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 table content://com.google.provider.NotePad/notes/ Refer to the tenth note content://com.google.provider/NotePad/notes/10
49
Register Content 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) <provider android:name="fooProvider" android:authorities="com.company.fooProvider" /> Example: <provider android:name=" com.acorns.LessonProvider" android:authorities="com.acorns.provider.Lessons" /> Another application example: <provider android:name="NotePadProvider" android:authorities="com.google.provider.NotePad" />
50
Provider Intent Filter
<activity android:name="NotesList" <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <action android:name="android.intent.action.VIEW" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter><intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" > </intent-filter> </activity>
51
Content Resolver Interact with a content provider Definition: A Content Resolver is an Android class that matches the URI to an available Content Provider that can handle the data Purpose: Separate provider (controller) from the data (model), enabling multiple content providers to be 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.getContentResolver(); Uri newUri = resolver.insert(Notepad.Notes.CONTENT_URI, values); Note: values is an instance of the ContentValues class
52
Find media titles and albums
Using the Android provided Media Store Content Provider String[] projection = { MediaStore.Audio.AudioColumns.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.Audio.AudioColumns.ALBUM); int tX = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TITLE); String[] result = new String[cursor.getCount()]; while (cursor.moveToNext()) { String title = cursor.getString(tX), album = cursor.getString(aX); result[cursor.getPosition()] = title + " (" + album + ")"; } cursor.close(); IllegalArgumentException Other Native Content Providers: Browser, Contacts, Calendar, Call log
53
Accessing the Contact List
int iDCol = ContactsContract.Contacts.ID; int nameCol = ContactsContract.Contacts.DISPLAY_NAME; Uri uri = ContactsContract.Contacts.CONTENT_URI; String[] projection = { iDCol, nameCol }; Cursor cursor = getContentResolver().query(uri, projection, null, null, null); int nameIndex = cursor.getColumnIndexOrThrow(nameCol); int idIndex = cursor.getColumnIndexOrThrow(contactIDCol); String[] result = new String[cursor.getCount()]; while(cursor.moveToNext()) { String name = cursor.getString(nameIndex); String id = cursor.getString(idIndex); result[cursor.getPosition()] = name + " (" + id + ")"; } cursor.close();
54
Searchable 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
55
Making 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 (or SearchFragment) 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 doesn’t contain a dedicated search button, add a menu option or widget that calls onSearchRequested() Or use the SearchView widget
56
Searchable configuration in res/xml
Filename: searchable.xml <?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android= > </searchable> 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
57
Searchable launch in Manifest
<application> … <activity android:name=".SearchActivity“ android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> <meta-data android:name="android.app.searchable" /> </activity> Note: singleTop prevents multiple instances on the back stack Note: launched when search is activated
58
Search Activity @Override public 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(SearchManager.QUERY); doMySearch(query); // Process the request } }
59
User selects item to download
Services Downloading Threads (maybe thread pool?) Main Activity Service onButtonClick() method gets control in the executing activity Activity uses an intent to request the download from the Service!) Worker Thread (downloading) New worker thread Done downloading! Start new worker thread User selects item to download 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 requests services Service starts worker threads to perform requests Bind services to the main thread Callbacks to the main activity completes transactions.
60
Creating a Service Create a class extending the service class
Register the service in the application manifest <service android:enabled="true" android:name=".MyService" android:permission="foo.bar.MY_SERVICE_PERMISSION"/> Start and stop of the service in the main activity Use separate threads for compute-bound operations Binding the service to the main activity enables the main activity to call service class methods and then update its user interface views Self-terminate the service when it is no longer needed Note: the permission an entity must have for the launch to succeed
61
Starting and Stopping Services
Explicitly start My Service Intent intent = new Intent(this, MyService.class); startService(intent); } Implicitly start a music Service Intent intent = new Intent(MyMusicService.PLAY_ALBUM); intent.putExtra(MyMusicService.ALBUM_NAME_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));
62
Creating 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 // Binder implements IBinder { LocalService getService() { return LocalService.this; } } @Override public void onCreate() { // TODO: Actions to perform when service is created. } // Return instance after activity call onBindService() call @Override public IBinder onBind(Intent intent) { return binder; } /* method for clients */ public int getRandomNumber() { return Math.random(); } }
63
public class BindingActivity extends Activity // An Activity using a Bound Service { LocalService service; private ServiceConnection connection = new ServiceConnection() // Anonymous instance public void onServiceConnected(ComponentName name, IBinder binder) { service=((LocalBinder)binder).getService(); public void onServiceDisconnected(ComponentName arg0) { service = null; } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); protected void onStart() { super.onStart(); bindService(new Intent(LocalService.class), connection, Context.BIND_AUTO_CREATE); protected void onStop() ` { super.onStop(); if (service != null) { unbindService(connection); service = null; } } public void onButtonClick(View v) { if (service!=null) { int num = service.getRandomNumber(); Toast.makeText(this, "number: "+num, Toast.LENGTH_SHORT).show(); } } }
64
Updating the GUI from a Thread
The activity registers a listening object Use a Handler in an activity to process queue of runnables private final Handler handler = new Handler(); 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
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.