1.27k likes | 1.49k Views
Mobile Programming Lecture 10. ContentProviders. Lecture 9 Review. In creating a bound service, why would you choose to use a Messenger over extending Binder? What are the differences between using GPS provider and Network provider? When should you stop listening for location updates?
E N D
Mobile Programming Lecture 10 ContentProviders
Lecture 9 Review • In creating a bound service, why would you choose to use a Messenger over extending Binder? • What are the differences between using GPS provider and Network provider? • When should you stop listening for location updates? • When should you disable a Sensor?
Lecture 9 Review • How do you start an Activity B from and Activity A, and get a result back from Activity B when B has completed? • How can you find out the structure of the intent filter for a given intent in the system?
Agenda • ContentProviders • Querying existing databases • Creating a database for your app • Manipulating data: insert, update, and delete • Content provider permissions
Android Application Components • Activity • Broadcast Receiver • Content Provider • Service
Content Provider Basics • A content provider manages access to a central repository of data • content providers are primarily intended to be used by other applications, which access the provider using a provider client object • A content provider presents data to external applications as one or more tables that are similar to the tables found in a relational database
Content Provider Basics • When you want to access data in a content provider, you must use the ContentResolver object in your application's Context to communicate with the provider as a client • The ContentResolver object communicates with the provider object, an instance of a class that implements ContentProvider • The provider object receives data requests from clients, performs the requested action, and returns the results.
Content Provider Basics • You don't need to develop your own provider if you don't intend to share your data with other applications, instead • Use SharedPreferences • The reason you use a ContentProvider is • data is too complex for SharedPreferences • expose your data to other applications
Content Provider Basics • _ID column serves as the primary key column that the content provider automatically maintains • Here's an example of a table with an _ID column
Contacts ContentProvider In our examples, we will look at the Contacts ContentProvider A lot of the work for a query goes into reading the documentation online http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html
Requesting Permission In order to read the user's contacts, you need to request permission first android.permission.READ_CONTACTS
Constructing a Query Let's build several queries for ContractsContract.Contacts The columns can be found in the documentation here A few of the columns are ...
Constructing a Query We will use the following fields in our class in our examples public class ContactsContractExampleActivity extends ListActivity { Cursor mCursor; CursorAdapter mCursorAdapter; String[] mProjection; String[] mListColumns; String mSelectionClause; String[] mSelectionArgs; String mOrderBy; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } }
Constructing a Query We will use the following fields in our class in our examples public class ContactsContractExampleActivity extends ListActivity { Cursor mCursor; CursorAdapter mCursorAdapter; String[] mProjection; String[] mListColumns; String mSelectionClause; String[] mSelectionArgs; String mOrderBy; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } } Note that we're extending ListActivity, so we don't need to add a ListView to our XML layout file. We don't even need an XML layout file
Constructing a Query Consider the following SQL queries
Constructing a Query - Query 1 SELECT * FROM ContactsContract.Contacts "Get every column for every contact in this database table"
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); }
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } When you want to access data in a content provider, you need to use a ContentResolver. You can get the ContentResolver by calling getContentResolver()
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } It returns a Cursor object.
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Using a Cursor object, you can call the query() method to execute a query on a content provider.
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The second argument is a String array (i.e. String[ ]) of which columns we want to be returned by the query. Passing null means return all columns, i.e. SELECT *
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The first argument to Cursor.query() is a Uri. It specifies the table that you want to access, i.e. SELECT * FROM table
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } ContactsContract is a content provider. You can think of it as the database
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } ContactsContract.Contacts is a content provider. You can think of it as a table in the database
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } This is one of the Uris for the table, which says how you want to access the table. Some tables have multiple Uris
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The third argument is a String. Here you specify the conditions for your query. Passing null means don't specify any conditions
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The fourth argument is a String[ ]. We will get back to this soon
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The fifth argument is a String. It says how we want to sort our results. Passing null means don't specify any sorting
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } SELECT * FROM ContactsContract.Contacts
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Our query is now complete, and the Cursor can iterate through the results now. But since we want to attach our results to a ListView, we need to add a few more lines of code
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Although our query was on all columns, here we create a String array of the columns we want to have displayed in our ListView.
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The column names are usually constants that you can reference via the database table
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } The column names are usually constants that you can reference via the database table
Constructing a Query - Query 1 In order to setup our ListView properly, we need to create an XML layout file that can represent each row in the list. We create a Layout XML file called query1.xml, which as has following TextView
Constructing a Query - Query 1 <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contact_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />
Constructing a Query - Query 1 <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contact_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" /> Note the android:id attribute of the TextView
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); }
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } So, String[ ] mListColumns specifies which columns we want to select
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } and int[] mListItems is a corresponding array, telling us where to place the actual value of the DISPLAY_NAME
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } It will be placed in this TextView, whose android:id="@+id/contact_name"
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } We've been using ArrayAdapters with ListViews in the past, but here we use a SimpleCursorAdapter instead, because we have a Cursor (i.e. mCursor)
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } First argument is a Context
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Second argument is our Layout XML file resource used to construct the ListView
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Third argument is our Cursor object
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Fourth argument is our String array of column names
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Fifth argument is our int array of TextView resources
Constructing a Query - Query 1 @Override public void onCreate(Bundle savedInstanceState) { mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); setListAdapter(mCursorAdapter); } Finally, we call setListAdapter and pass our SimpleCursorAdapter
Constructing a Query - Query 1 See the query1() method inside of ContactsContractQueryExample.tar This query is inefficient, because we're requesting all columns, but yet only using one column after we get the results
Constructing a Query - Query 2 Consider the following SQL query SELECT _ID, DISPLAY_NAME FROM ContactsContract.Contacts "Get the _ID and DISPLAY_NAME for all contacts"
Constructing a Query - Query 2 @Override public void onCreate(Bundle savedInstanceState) { mProjection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; mCursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, mProjection, null, null, null); mListColumns = new String[] { ContactsContract.Contacts.DISPLAY_NAME }; mListItems = new int[] { R.id.contact_name }; mCursorAdapter = new SimpleCursorAdapter(this, R.layout.query1, mCursor, mListColumns, mListItems); }