Skip to content

Writing your own ContentProvider

January 13, 2010

Hey everyone,

So the following tutorial will hopefully give you a good idea of how to implement your own ContentProvider. I know that there are a lot of pretty good sites out there with some good code snippets, but I’ve noticed that not many really help the developer understand what’s going on step by step so my goal is to not only provide an example of a fully implemented ContentProvider but also to step you through the process.

So I’ll start by just posting all of the code, and I’ll go through it slowly afterwards. We’ll start with the Custom Content Provider itself:


package jason.wei.apps.NotePad.providers;

import java.util.HashMap;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;

/**
 * @author Jason Wei
 * 
 */
public class NotesContentProvider extends ContentProvider {

    private static final String TAG = "NotesContentProvider";

    private static final String DATABASE_NAME = "notes.db";

    private static final int DATABASE_VERSION = 1;

    private static final String NOTES_TABLE_NAME = "notes";

    public static final String AUTHORITY = "jason.wei.apps.notes.providers.NotesContentProvider";

    private static final UriMatcher sUriMatcher;

    private static final int NOTES = 1;

    private static final int NOTES_ID = 2;

    private static HashMap<String, String> notesProjectionMap;

    private static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " (" + Notes.NOTE_ID
                    + " INTEGER PRIMARY KEY AUTOINCREMENT," + Notes.TITLE + " VARCHAR(255)," + Notes.TEXT + " LONGTEXT" + ");");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + NOTES_TABLE_NAME);
            onCreate(db);
        }
    }

    private DatabaseHelper dbHelper;

    @Override
    public int delete(Uri uri, String where, String[] whereArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        switch (sUriMatcher.match(uri)) {
            case NOTES:
                break;
            case NOTES_ID:
                where = where + "_id = " uri.getLastPathSegment();
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        int count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    @Override
    public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
            case NOTES:
                return Notes.CONTENT_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues initialValues) {
        if (sUriMatcher.match(uri) != NOTES) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }

        ContentValues values;
        if (initialValues != null) {
            values = new ContentValues(initialValues);
        } else {
            values = new ContentValues();
        }

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        long rowId = db.insert(NOTES_TABLE_NAME, Notes.TEXT, values);
        if (rowId > 0) {
            Uri noteUri = ContentUris.withAppendedId(Notes.CONTENT_URI, rowId);
            getContext().getContentResolver().notifyChange(noteUri, null);
            return noteUri;
        }

        throw new SQLException("Failed to insert row into " + uri);
    }

    @Override
    public boolean onCreate() {
        dbHelper = new DatabaseHelper(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setTables(NOTES_TABLE_NAME);
        qb.setProjectionMap(notesProjectionMap);

        switch (sUriMatcher.match(uri)) {    
            case NOTES:
                break;
            case NOTES_ID:
                selection = selection + "_id = " uri.getLastPathSegment();
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);

        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int count;
        switch (sUriMatcher.match(uri)) {
            case NOTES:
                count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, NOTES_TABLE_NAME, NOTES);
        sUriMatcher.addURI(AUTHORITY, NOTES_TABLE_NAME + "/#", NOTES_ID);

        notesProjectionMap = new HashMap<String, String>();
        notesProjectionMap.put(Notes.NOTE_ID, Notes.NOTE_ID);
        notesProjectionMap.put(Notes.TITLE, Notes.TITLE);
        notesProjectionMap.put(Notes.TEXT, Notes.TEXT);
    }
}

And next a little helper class that keeps all of the columns organized and readily accessible:


public class Note {

	public Note() {
	}

	public static final class Notes implements BaseColumns {
		private Notes() {
		}

		public static final Uri CONTENT_URI = Uri.parse("content://"
				+ NotesContentProvider.AUTHORITY + "/notes");

		public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.jwei512.notes";

		public static final String NOTE_ID = "_id";

		public static final String TITLE = "title";

		public static final String TEXT = "text";
	}

}

(Note that this is similar to how Google provides you with the columns, i.e. People._ID, People.NUMBER, CallLog.CACHED_NAME, etc)

Okay so let’s go through this step by step. First, when you extend ContentResolver, there are 6 methods you need to overwrite:

query()
insert()
update()
delete()
getType()
onCreate()

Normally these are just wrapper functions around the raw SQL queries. For instance, the method:

db.delete(tableName, where, whereArgs);

Is simply just a wrapper around the SQL query that looks something like:

"delete from " + tableName + " where " + where + " ? " + whereArgs"

So for those who know SQL, if I wanted to delete the note with title “Hello World” then my queries would look like:

// wrapper query
db.delete(NOTES_TABLE_NAME, Notes.TITLE + "= ' " + "Hello World" + " ' ", null);

// real query when translated
String query = "delete from notes where title = 'Hello World' ";

And so as you look at how I overwrite the 6 methods, you’ll see that I’m simply taking in the parameters and inputting them appropriately into the wrapper methods. Of course, you can override them as you like depending on what you want your application to do. Perhaps your application is ONLY worried about retrieving the average of some numbers. Then in your query() method, maybe instead of querying for the numbers normally, and then always having to iterate through the numbers and find the average from the JAVA side, you could customize your query to automatically return the average BY DEFAULT.

But in case you’re still confused, let’s look at an example:

public int delete(Uri uri, String where, String[] whereArgs) {
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    switch (sUriMatcher.match(uri)) {
        case NOTES:
            break;
        case NOTES_ID:
            where = where + "_id = " uri.getLastPathSegment();
            break;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
    }

    int count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
    getContext().getContentResolver().notifyChange(uri, null);
    return count;
}

So basically you pass in the Uri (in our case it will be Notes.CONTENT_URI to tell the system which table you’re going to target) and if the Uri matches NOTES then we use our SQLiteDatabase (retrieved in the first line) to delete from the notes table using the where arguments passed in. Another option we have is to match against the Uri NOTES_ID, which will allow us to easily embed a specific note ID to delete. Practically, the difference is that the former Uri allows you all the flexibility you want as you can make your where string match any range of notes to be deleted. The latter is more for identifying specific notes and allows the client (i.e. the developer using the ContentProvider) to not have to worry about even passing in a where argument.

A similar idea goes into overriding query(), insert(), and update(), and in our onCreate() method we are simply using our SQLiteDatabase to execute a CREATE TABLE statement where we build our table using our defined columns (i.e. Notes.NOTE_ID, Notes.TITLE, and Notes.TEXT) and also make sure we use the proper SQLite3 column types.

One thing to note here is that Android requires that the unique id column have name “_id”.

This is extremely important as otherwise your content provider will not get registered properly.

Now, one last thing before I get to how you register it in your Manifest is what this AUTHORITY String is all about. In my case it was:

public static final String AUTHORITY = "jason.wei.apps.notes.providers.NotesContentProvider";

So the authority you can more or less define in anyway that you like, but typically it will look something like:

{package name}.providers.{Custom Provider name}

For organizational purposes, this means that in your Android project, you will have to create a new package called:

{package name}.providers

In order for the Uri path to be correct (again pretty obvious, but who knows).

And so this AUTHORITY basically just helps you define the CONTENT_URI of your ContentProvider (i.e. the path to your database), and you will register your provider in your Manifest like so:

<provider android:name="jason.wei.apps.notes.providers.NotesContentProvider" android:authorities="jason.wei.apps.notes.providers.NotesContentProvider" />

And so you see that I define my content providers in a way such that the project path is equal to the authorities path (again, not required, but probably recommended for simplicity and organizational issues).

And that’s it! Now you have a fully registered and created ContentProvider. My next example/tutorial will show you how to write cool wrapper functions that help you make queries and manipulate data in whatever way you want!

Let me know if you have questions, otherwise Happy Coding.

– jwei

59 Comments leave one →
  1. Mamadou permalink
    January 26, 2010 1:16 am

    Very helpfull, clear, it’s 5 th tuime i read tutos on this, but this time i really understand

  2. iris permalink
    April 2, 2010 2:19 am

    Hi,

    Thanks for tutorial. Its very helpful. But I have a question,

    In line 106: long rowId = db.insert(NOTES_TABLE_NAME, Notes.TEXT, values);
    you have Notes.TEXT, what about the TITLE? I am new to this, I have a table with four columns, I want to know why do we have only the text value being entered? I understand that in the Notes example, there is only a title and a text for each Note, is it that the title gets appended to the uri? I am a bit lost here. How do I deal with a db with more columns?

    Thank you!

    • April 2, 2010 11:07 am

      Hey iris,

      Sorry for not being clear. If you read the javadoc for what db.insert() does you’ll see that the second entry has the following description:

      nullColumnHack – SQL doesn’t allow inserting a completely empty row, so if initialValues is empty, this column will explicitly be assigned a NULL value

      Hence you should actually be able to put any column into that field (i.e. Notes.TITLE, etc) and basically it means that column will get instantiated to null if initialValues is passed in as empty.

      Of course this should never happen, and it’s probably good practice if you check for empty insertions earlier and prevent it from getting inserted, but yes hope this answered your question.

  3. Mike permalink
    April 13, 2010 3:19 am

    hi jwei512,

    i have created my own ContentProvider successfully. but now i want to use it in another android application. since i know the authority i did something like this

    Uri books = Uri.parse(“content://com.sabretch.colorEyeD/color”);
    Cursor cur = managedQuery(books, null, null, null, null);

    but how can i find out the column details here in the query i want to pass a where clause how can i do that???

    regards,
    Mike

    • mobibob permalink
      May 26, 2011 10:11 am

      The first null in your call to managedQuery is a projection of the fields that you want to display. you cannot “discover” them, but if you specified “null” (as you have) you can loop through your cursor’s column index and get the column value by index. This useful for reverse engineering the CP.

  4. hany permalink
    April 27, 2010 1:40 am

    Thanks for this tutorial .

    I am new to android . I want store an image in content provider and retrive it .
    please help me in this problem .

  5. hany permalink
    April 27, 2010 9:36 pm

    Thanks for this tutorial .

    I am new to android , I want to store an image in cotent provider and retrive back ,

    please help this problem , please send smple code .

    Thank you.

  6. mamila permalink
    May 7, 2010 12:51 am

    Thanks for the helpful post on content provider
    But I’ve one more question: How can we include several tables and query them in different activities within the same application?

    • May 9, 2010 6:27 pm

      Hey mamila,

      Each Content Provider (as shown in the example) creates one table – so, to customize several tables just write different content provider classes for each one.

      Now to query them in different applications, you’ll want to look into Cursors (http://stackoverflow.com/questions/903343/cursor-get-the-field-value-android). That’ll let you query them in different activities.

      Hope this helps

      – jwei

      • May 10, 2010 7:29 pm

        @jwei512, @mamila

        Are you sure about that? Isn’t it possible to have different tables exposed via different URIs?

        This seems to be what they are saying here:
        http://developer.android.com/guide/topics/providers/content-providers.html#creating

        Although I’m not certain.

        I have the same problem as mamila and have yet to get a straight answer yet. I really appreciate your tutorial here, it’s been very useful, but I’m not sure about having one Content Provider per Table.

        Your input would be appreciated!

        Rich

      • May 12, 2010 2:27 am

        Hey Rich / Mamila,

        Sorry let me be more clear. It’s not that you can only have one table per content provider – you’re right in saying that you can expose different tables via different URIs which in theory can be created in the same content provider.

        However in general (at least for me) it makes sense to keep it at one “sub” URI per content provider class (by “sub” URI I mean if it were content://jason.wei.apps/notes and content://jason.wei.apps/notes/titles as the two URIs then obviously these two are related and could/should go in the same content provider). So, for instance, if I had one table for notes and one table users, in theory I could write a single content provider that just does a bunch of case checking and uses a bunch of switch statements so that given the correct URI the correct query, etc, is performed. But for me it just makes sense to create some kind of NoteContentProvider.class and UserContentProvider.class to separate the two.

        Does this make sense? Sorry if it seems like I’m rambling.

        I really need to get some kind of forum for this stuff =P

        – jwei

  7. May 14, 2010 2:30 am

    Yes. This is the same picture I had in my head, too. Thank you for replying to me and clarifying this.

    Nice blog, I appreciate all the content here. Have bookmarked, will stop by again.

  8. Chua Wei Ting permalink
    June 30, 2010 9:24 pm

    so sorry, i learn things quite slowly and kinda dont understand.

    what path is this->
    jason.wei.apps.notes.providers.NotesContentProvider(?)

    if im doing my own content provider what is the path?

    • ashraf permalink
      September 14, 2010 4:05 am

      its the provider package.class name. you can user own…

  9. Chua Wei Ting permalink
    June 30, 2010 11:37 pm

    Hi there! Jwei512, Ive created the whole ContentProvider and change it to my own. However, I still have errors!

    I’m doing this contentprovider to show my data to a list view. I need your help.
    If possible, please email me?

    chuaisarocker@hotmail.com

    thanks!

    • July 22, 2010 12:00 am

      Hi! Really Good Blog, Giving Detailed information

      Please may you tell me how to add more than One Content Provider to a single
      mainfest file

      • July 22, 2010 4:48 am

        Just simply register multiple providers – just as how you would register multiple Activities or Services in the manifest.

        – jwei

  10. September 8, 2010 10:05 pm

    Thanks jwei512 for a nice tutorial … 🙂

  11. Arpit permalink
    September 10, 2010 4:21 am

    In the extension of ContentProvider, the query method has one issue which is sort of a strange design for me…

    You create SQLiteDatabase instance and then query the content and then return the Cursor object. You never close the SQLiteDatabase instance.

    For Android documentation it states the DB is cached, but it also states to close the DB…How and when does that happens??? Please let me know as I have written different applications and don’t want them to come out with some SQL Exception cause too many SQLiteDatabase instance exists in memory which are not closed.

  12. Satish Kumar SN permalink
    September 13, 2010 1:22 am

    Very good tutorial for new comer. I have one question how to use the our custom provider in another package. Can you please provide any document this how to use in another package.
    As we are using android contact content provider.

  13. ashraf permalink
    September 14, 2010 4:04 am

    Good!
    I read the Busy Coder Guide to Android , online dev tutorials, but could not figure out to make a content provider. I got it cleared from here.
    Very clean tutorial and example.
    However, having the provider in

    {package name}.providers.{Custom Provider name}
    is not necessary.
    AND, can you tell me is it possible to give restriction on the provider so nothing else than my application should access it?

  14. September 24, 2010 1:40 pm

    Sorry for this very basic question. If I have a database on a remote server, can I still define that as a URI? Or do I have to create a SQLlite db on the Android and have THAT access the remote server?

    If I CAN directly access the remote server, and again sorry to be dense, what would the db declaration look like?

    • September 25, 2010 10:14 am

      Hey Emily,

      Sorry I actually don’t know much about interacting with remote servers and remote databases.

      In my experience, you can define a URI for local SQLite databases, but for remote servers/databases I’ve always used things like REST calls to access them.

      Sorry if this didn’t help I wonder if anyone else would know about this…

      – jwei

      • September 25, 2010 12:17 pm

        Thanks for your response.

        I did implement it as a REST call, which works fine. Just thought it would be more elegant to do it as a ContentProvider.

  15. Lior permalink
    September 26, 2010 9:13 am

    Great tutorial!

    Can I use the same method to overwrite an existing Android contentProvider? for example the Bookmarks?

    I want to resort the Android Bookmarks db according to the user selected order (orderBy) – can this be done with overwriting the “cursor query” function?

  16. hema permalink
    October 27, 2010 3:27 am

    hi

    I am new in android i am stored student data in content providers
    please send me code for my mail

    Thanks&Regards
    hemalatha

  17. Cathy permalink
    November 2, 2010 8:29 am

    This is a really helpful tutorial – thanks. One question though:
    You said, “One thing to note here is that Android requires that the unique id column have name “_id”.”
    Does this mean that android doesn’t allow composite keys (i.e. where two columns in the table are combined to make the primary key) ? If so, is there a way around this – using the ProjectionMap somehow?

  18. Tikam permalink
    November 10, 2010 12:07 am

    Hello Sir , this is very good example for learning . same thing i was trying. but when i execute my own content provider its showing this error

    11-10 13:32:47.119: ERROR/ActivityThread(390): Failed to find provider info for com.xymob.bhilai.nameandfeedback
    please give me any solution for that .
    i was made one activity for insert the record through content resolver
    thanks
    tikam

  19. Ryan permalink
    December 1, 2010 8:55 am

    Say I want a ListView populated from a custom database that has names and phone numbers. I could use an adapter to populate that list from a custom ContentProvider that I write. But my database is set up in two tables (person: _id, firstname, lastname) (number: _id, person_id, phonenumber). (I know this is a really bad example).

    In this case I would want a Content Provider that returned a list that was the result of a JOIN of the two tables. So I would think that I could write my CP to return the result of the join, and then for a delete maybe would implement a delete from the number table, and for an add would add to the number table.

    From the Activity’s point of view, it doesn’t know about the schema of the DB, that I’ve broken the data into two tables. It just knows that it can get a list of the people with their numbers and add/delete from the numbers.

    I could also provide the CP to other apps if needed and they too would be shielded from the underlying DB schema.

    Aside from my example being really lame, is this the intent of the ContentProvider? That I can expose logical content elements even if they don’t physically match the implementation in the DB (assuming I can make the delete/insert logic make sense for my app)? Or is it good practice to really have one CP for each table in the DB?

    Thanks! Great article!

  20. Ailin permalink
    January 26, 2011 6:21 pm

    Hi! I’ve implemented this ContentProvider but I have an IllegalArgumentException Unknow URI content://….

  21. Marko M permalink
    February 14, 2011 12:07 am

    Great tutorial, example is good explained, it helps a lot 😉

  22. Nikko permalink
    March 6, 2011 1:38 pm

    Hi,

    Thanks for this nice tutorial. I just do not understand one thing. What’s the “CONTENT_TYPE” constant you set in the Notes helper class ? I mean, how do you know which value it should take ?

    Thanks in advance

  23. Paulo Roberto Neves Almeida permalink
    March 27, 2011 4:11 am

    Tremendues dude, very well explayned

  24. April 5, 2011 1:55 pm

    Great tutorial, thanks. However, would suggest that you close your databases after you’re done using them via getWritableDatabase()/getReadableDatabase().

  25. June 10, 2011 9:28 am

    Thank you so much! Old post, but I am a newbie and this was what I needed for my job! Just wanted to thank you for the time putting the database stuff up!

  26. June 18, 2011 6:17 am

    thank you for this stuff

  27. July 11, 2011 8:13 pm

    Pretty good! Thanks for your sharing!

    There is one thing that I’m a little confused. What’s the meaning of “CONTENT_TYPE”? I mean what type it is? What’s your “vnd.android.cursor.dir/vnd.jwei512.notes” stands for?

    Thanks in advance.

  28. p6majo permalink
    July 24, 2011 1:26 pm

    Would the ContentProvider be helpful to access and modify a database that’s stored on the SdCard? What would be the appropriate uri?

  29. Nasrudeen permalink
    August 19, 2011 2:08 am

    Thanks

  30. Richa permalink
    September 15, 2011 1:41 am

    very informative…
    Thanx

  31. babu permalink
    November 24, 2011 10:20 pm

    Thanks for a very good tutorial

  32. MasterOfPuppetz permalink
    January 27, 2012 7:19 pm

    Hey,
    just have two annotations to make:

    1. You may have explicitly mentioned to put the -Tag into the -Tag of the Manifest and not just anywhere.

    2. I get an Exception:
    java.lang.ClassNotFoundException: jason.wei.apps.notes.providers.NotesContentProvider

    I guess this is because your package path doesn’t really match your AUTHORITY as you said!?
    See:
    001 package jason.wei.apps.securenotes.providers;
    034 public static final String AUTHORITY = “jason.wei.apps.notes.providers.NotesContentProvider”;

    Can’t say I found the source of my problem through this or any other Tutorial concerning this Topic, but yours was the most simple and precise one and never the less it helped me, so thanks!

  33. Priyanka permalink
    February 3, 2012 2:23 am

    When does the onCreate() of DatabaseHelper class gets called ??
    After or before onCreate() of content provider ?

  34. Darryl permalink
    March 7, 2012 12:25 pm

    Great information but I have one question you outlined that you use the “SQLiteDatabase to execute a CREATE TABLE statement where we build our table using our defined columns (i.e. Notes.NOTE_ID, Notes.TITLE, and Notes.TEXT) and also make sure we use the proper SQLite3 column types.” Then you said that Android required the unique id field to be “_id”, does tou “NOTE_ID” field fit this criteria?

    • March 13, 2012 10:14 am

      Hey Darryl,

      Yes – the NOTE_ID variable should be set to “_id”.

      – jwei

  35. Anuj Goyal permalink
    June 16, 2012 3:56 pm

    Are there any tutorials on writing a ContentProvider if you already have a sqlite database that you are just reading from?

    • June 16, 2012 8:04 pm

      Hi Anuj,

      Sorry don’t think I understand your question. Could you provide more details / rephrase it?

      – jwei

  36. August 9, 2012 9:52 pm

    Not a bad tutorial, but you have some code that won’t compile:

    124 switch (sUriMatcher.match(uri)) {
    125 qb.setTables(NOTES_TABLE_NAME); // this line and the one below it don’t belong here
    126 qb.setProjectionMap(notesProjectionMap);
    127 case NOTES:
    128 break;
    129 case NOTES_ID; // <- should be a colon
    130 selection = selection + "_id = " uri.getLastPathSegment();
    131 break;
    132 default:
    133 throw new IllegalArgumentException("Unknown URI " + uri);
    134 }

    And some code that's wrong:

    066 switch (sUriMatcher.match(uri)) {
    067 case NOTES:
    068 break;
    069 case NOTES_ID;
    070 where = where + "_id = " uri.getLastPathSegment();
    // Oops, you want a break here
    071 default:
    072 throw new IllegalArgumentException("Unknown URI " + uri);
    073 }

    Did you run this before posting it?

    • August 10, 2012 5:48 am

      Hey Benson,

      Yea I actually updated this post a few days ago (added the /uri/# path) so that it would be consistent with my new “Grant Uri Permissions” post. Unfortunately I updated it on the fly so didn’t get a chance to compile/run the new changes.

      Thanks for catching these – should be fixed now.

      – jwei

  37. December 3, 2012 7:20 am

    Hi
    I have a custom content provider but how do I use it in another apk ? I tried importing the complete package but It is not accessible.
    Please help
    Nisha

    • December 3, 2012 10:00 am

      Hi Nisha,

      You simply need to pass the URI of your custom content provider into the query() methods – i.e. it doesn’t care which application is making the query as long as the URI is correct it will return a cursor pointing to the data.

      – jwei

  38. December 15, 2012 12:15 pm

    I’m not that much of a internet reader to be honest but your blogs really nice, keep it up!
    I’ll go ahead and bookmark your website to come back later on. Cheers

  39. Vamsi permalink
    April 30, 2013 9:02 pm

    inserting into the database works…have not encountered any errors there…querying doesnot work…throws an IllegalStateException…what am I doing wrong.

Trackbacks

  1. Think Android
  2. PN.Truong Phuc's Blog
  3. Granting Content Provider URI Permissions « Think Android
  4. Handling multiple dbs in android : Android Community - For Application Development

Leave a reply to jwei512 Cancel reply