Android content provider example

Add the following in application tag in the manifest file, adjust the package name accordingly.

<activity android:name=".content_provider.views.ContentProviderActivity"
    android:parentActivityName=".MainActivity"/>


<provider
    android:authorities="com.example.androidcomponents"
    android:name=".content_provider.data.VersionsProvider"
    android:exported="false"
    android:syncable="true"/>

activity_content_provider.xml, layout file for the content provider main screen

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:id="@+id/fragment_container"
    tools:layout="@layout/fragment_content_provider" android:layout_width="match_parent"
    android:layout_height="match_parent" />

fragment_content_provider.xml, layout file for the content provider fragment

<?xml version="1.0" encoding="utf-8"?>
<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lv_versions"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>

fragment_version_detail.xml, layout file for the version detail screen.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <include layout="@layout/version_item"/>

    <TextView
        android:id="@+id/tv_uri"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_version_description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

version_item.xml, layout file for the version item in the list view.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv_version_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textStyle="bold"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:gravity="right"
            android:text="Version Code:"/>
        <TextView
            android:id="@+id/tv_version_code"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:layout_weight="5"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:gravity="right"
            android:text="Api Level:"/>
        <TextView
            android:id="@+id/tv_api_level"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:layout_weight="5"/>
    </LinearLayout>

</LinearLayout>

Version.java, the model/pojo class for holding the Android version information.

public class Version {
    private String versionName;
    private String versionCode;
    private String apiLevel;
    private String description;

    public Version(String name, String version, String apiLevel, String description){
        this.versionName = name;
        this.versionCode = version;
        this.apiLevel = apiLevel;
        this.description = description;
    }

    public String getVersionName() {
        return versionName;
    }

    public String getVersionCode() {
        return versionCode;
    }

    public String getApiLevel() {
        return apiLevel;
    }

    public String getDescription() {
        return description;
    }
}

VersionsContract.java, the contract for database table and content provider.

public class VersionsContract {

	public static final String CONTENT_AUTHORITY = "com.example.androidcomponents";

	public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);


	public static final class VersionEntry implements BaseColumns {
		// table name
		public static final String TABLE_VERSIONS = "versions";
		// columns
		public static final String _ID = "_id";
		public static final String COLUMN_DESCRIPTION = "description";
		public static final String COLUMN_VERSION_NAME = "version_name";
		public static final String COLUMN_VERSION_CODE = "version_code";
		public static final String COLUMN_API_LEVEL = "api_level";

		// content uri
		public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_VERSIONS).build();

		// cursor of base type directory for multiple entries
		public static final String CONTENT_DIR_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + TABLE_VERSIONS;

		// cursor of base type item for single entry
		public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +"/" + CONTENT_AUTHORITY + "/" + TABLE_VERSIONS;

		// insertion URIs
		public static Uri buildVersionsUri(long id){
			return ContentUris.withAppendedId(CONTENT_URI, id);
		}
	}
}

VersionsDBHelper.java, the sqlite database helper class.

public class VersionsDBHelper extends SQLiteOpenHelper {
	public static final String LOG_TAG = VersionsDBHelper.class.getSimpleName();

	private static final String DATABASE_NAME = "android_versions.db";
	private static final int DATABASE_VERSION = 2;

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

	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase) {
		final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " +
				VersionsContract.VersionEntry.TABLE_VERSIONS + "(" + VersionsContract.VersionEntry._ID +
				" INTEGER PRIMARY KEY AUTOINCREMENT, " +
				VersionsContract.VersionEntry.COLUMN_VERSION_NAME + " TEXT NOT NULL, " +
				VersionsContract.VersionEntry.COLUMN_VERSION_CODE + " TEXT NOT NULL, " +
				VersionsContract.VersionEntry.COLUMN_API_LEVEL + " TEXT NOT NULL, " +
				VersionsContract.VersionEntry.COLUMN_DESCRIPTION + " TEXT NOT NULL);";

		sqLiteDatabase.execSQL(SQL_CREATE_MOVIE_TABLE);
	}

	// Upgrade database when version is changed.
	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
		Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
				newVersion + ". OLD DATA WILL BE DESTROYED");

		// Drop the table
		sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + VersionsContract.VersionEntry.TABLE_VERSIONS);
        sqLiteDatabase.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
                VersionsContract.VersionEntry.TABLE_VERSIONS + "'");

		// re-create database
		onCreate(sqLiteDatabase);
	}
}

VersionsProvider.java, finally the content provider class for providing the version information.

public class VersionsProvider extends ContentProvider {
    private static final String LOG_TAG = VersionsProvider.class.getSimpleName();
    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private VersionsDBHelper mOpenHelper;

    // Codes for the UriMatcher
    private static final int VERSION = 100;
    private static final int VERSION_WITH_ID = 200;

    private static UriMatcher buildUriMatcher() {
        // Build a UriMatcher by adding a specific code to return based on a match
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = VersionsContract.CONTENT_AUTHORITY;

        // add a code for each type of URI you want
        matcher.addURI(authority, VersionsContract.VersionEntry.TABLE_VERSIONS, VERSION);
        matcher.addURI(authority, VersionsContract.VersionEntry.TABLE_VERSIONS + "/#", VERSION_WITH_ID);

        return matcher;
    }

    @Override
    public boolean onCreate() {
        mOpenHelper = new VersionsDBHelper(getContext());
        return true;
    }

    @Override
    public String getType(Uri uri) {
        final int match = sUriMatcher.match(uri);

        switch (match) {
            case VERSION: {
                return VersionsContract.VersionEntry.CONTENT_DIR_TYPE;
            }
            case VERSION_WITH_ID: {
                return VersionsContract.VersionEntry.CONTENT_ITEM_TYPE;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor retCursor;
        switch (sUriMatcher.match(uri)) {
            // All versions selected
            case VERSION: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        VersionsContract.VersionEntry.TABLE_VERSIONS,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder);
                return retCursor;
            }
            // Individual version based on Id selected
            case VERSION_WITH_ID: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        VersionsContract.VersionEntry.TABLE_VERSIONS,
                        projection,
                        VersionsContract.VersionEntry._ID + " = ?",
                        new String[]{String.valueOf(ContentUris.parseId(uri))},
                        null,
                        null,
                        sortOrder);
                return retCursor;
            }
            default: {
                // By default, we assume a bad URI
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        Uri returnUri;
        switch (sUriMatcher.match(uri)) {
            case VERSION: {
                long _id = db.insert(VersionsContract.VersionEntry.TABLE_VERSIONS, null, values);
                // insert unless it is already contained in the database
                if (_id > 0) {
                    returnUri = VersionsContract.VersionEntry.buildVersionsUri(_id);
                } else {
                    throw new android.database.SQLException("Failed to insert row into: " + uri);
                }
                break;
            }

            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);

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

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        int numDeleted;
        switch (match) {
            case VERSION:
                numDeleted = db.delete(VersionsContract.VersionEntry.TABLE_VERSIONS, selection, selectionArgs);
                // reset _ID
                db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
                        VersionsContract.VersionEntry.TABLE_VERSIONS + "'");
                break;
            case VERSION_WITH_ID:
                numDeleted = db.delete(VersionsContract.VersionEntry.TABLE_VERSIONS,
                        VersionsContract.VersionEntry._ID + " = ?",
                        new String[]{String.valueOf(ContentUris.parseId(uri))});
                // reset _ID
                db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
                        VersionsContract.VersionEntry.TABLE_VERSIONS + "'");

                break;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }

        return numDeleted;
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        switch (match) {
            case VERSION:
                // allows for multiple transactions
                db.beginTransaction();

                // keep track of successful inserts
                int numInserted = 0;
                try {
                    for (ContentValues value : values) {
                        if (value == null) {
                            throw new IllegalArgumentException("Cannot have null content values");
                        }
                        long _id = -1;
                        try {
                            _id = db.insertOrThrow(VersionsContract.VersionEntry.TABLE_VERSIONS, null, value);
                        } catch (SQLiteConstraintException e) {
                            Log.w(LOG_TAG, e.toString());
                        }
                        if (_id != -1) {
                            numInserted++;
                        }
                    }

                    if (numInserted > 0) {
                        // If no errors, declare a successful transaction.
                        // database will not populate if this is not called
                        db.setTransactionSuccessful();
                    }
                } finally {
                    // all transactions occur at once
                    db.endTransaction();
                }
                if (numInserted > 0) {
                    // if there was successful insertion, notify the content resolver that there was a change
                    getContext().getContentResolver().notifyChange(uri, null);
                }
                return numInserted;
            default:
                return super.bulkInsert(uri, values);
        }
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        int numUpdated = 0;

        if (contentValues == null) {
            throw new IllegalArgumentException("Cannot have null content values");
        }

        switch (sUriMatcher.match(uri)) {
            case VERSION: {
                numUpdated = db.update(VersionsContract.VersionEntry.TABLE_VERSIONS,
                        contentValues,
                        selection,
                        selectionArgs);
                break;
            }
            case VERSION_WITH_ID: {
                numUpdated = db.update(VersionsContract.VersionEntry.TABLE_VERSIONS,
                        contentValues,
                        VersionsContract.VersionEntry._ID + " = ?",
                        new String[]{String.valueOf(ContentUris.parseId(uri))});
                break;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        if (numUpdated > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }

        return numUpdated;
    }

}

ContentProviderActivity.java, the main activity for show casing the content provider example.

public class ContentProviderActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_provider);
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
                new FragmentContentProvider()).commit();

    }
}

FragmentContentProvider.java, the main fragment for show casing the content provider example.

public class FragmentContentProvider extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{

    private static final String LOG_TAG = FragmentContentProvider.class.getSimpleName();

    private VersionAdapter mVersionAdapter;
    private ListView lvVersions;


    private static final int CURSOR_LOADER_ID = 0;
    Version[] versions = {
            new Version("Cupcake", "1.5", "3", "The first release of Android"),
            new Version("Donut", "1.6", "4", "The world's information is at your fingertips – search the web, get driving directions... or just watch cat videos."),
            new Version("Eclair", "2.0-2.1", "5-7", "Make your home screen just how you want it. Arrange apps and widgets across multiple screens and in folders. Stunning live wallpapers " + "respond to your touch."),
            new Version("Froyo", "2.2-2.2.3", "8", "Voice Typing lets you input text, and Voice Actions let you control your phone, just by speaking."),
            new Version("GingerBread", "2.3-2.3.7", "9-10", "New sensors make Android great for gaming - so you can touch, tap, tilt, and play away."),
            new Version("Honeycomb", "3.0-3.2.6", "11-13", "Optimized for tablets, this release opens up new horizons wherever you are."),
            new Version("Ice Cream Sandwich", "4.0-4.0.4", "14-15", "Android comes of age with a new, refined design. Simple, beautiful and beyond smart."),
            new Version("Jelly Bean", "4.1-4.3.1", "16-18", "Android is fast and smooth with buttery graphics. With Google Now, you get just the right information at the right time."),
            new Version("KitKat", "4.4-4.4.4", "19-20", "Smart, simple, and truly yours. A more polished design, improved performance, and new features."),
            new Version("Lollipop", "5.0-5.1.1", "21-22", "A sweet new take on Android. Get the smarts of Android on screens big and small – with the right information at the right moment."),
            new Version("Marshmallow", "6.0–6.0.1", "23", "Now on Tap anticipates what you need in the moment. With a simple tap, you can get cards with useful information and apps that feed your need to know."),
            new Version("Nougat", "7.0–7.1.2", "24–25", "With more ways to make Android your own, Android Nougat is our sweetest release yet.")
    };

    public FragmentContentProvider() {
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        Cursor c =
            getActivity().getContentResolver().query(VersionsContract.VersionEntry.CONTENT_URI,
            new String[]{VersionsContract.VersionEntry._ID},
                    null,
                    null,
                    null);
        if (c.getCount() == 0) {
            insertData();
        }
        // initialize loader
        getLoaderManager().initLoader(CURSOR_LOADER_ID, null, this);
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // inflate fragment_main layout
        final View rootView = inflater.inflate(R.layout.fragment_content_provider, container, false);

        // initialize our VersionAdapter
        mVersionAdapter = new VersionAdapter(getActivity(), null, 0);
        lvVersions = (ListView) rootView.findViewById(R.id.lv_versions);
        lvVersions.setAdapter(mVersionAdapter);

        lvVersions.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // increment the position to match Database Ids indexed starting at 1
                int uriId = position + 1;
                Uri uri = ContentUris.withAppendedId(VersionsContract.VersionEntry.CONTENT_URI, uriId);

                FragmentVersionDetail detailFragment = FragmentVersionDetail.newInstance(uriId, uri);
                getActivity().getSupportFragmentManager().beginTransaction()
                        .replace(R.id.fragment_container, detailFragment)
                        .addToBackStack(null).commit();
            }
        });


        return rootView;
    }

    // insert data into database
    public void insertData(){
        ContentValues[] versionValuesArr = new ContentValues[versions.length];
        for(int i = 0; i < versions.length; i++){
            versionValuesArr[i] = new ContentValues();
            versionValuesArr[i].put(VersionsContract.VersionEntry.COLUMN_VERSION_NAME, versions[i].getVersionName());
            versionValuesArr[i].put(VersionsContract.VersionEntry.COLUMN_VERSION_CODE, versions[i].getVersionCode());
            versionValuesArr[i].put(VersionsContract.VersionEntry.COLUMN_API_LEVEL, versions[i].getApiLevel());
            versionValuesArr[i].put(VersionsContract.VersionEntry.COLUMN_DESCRIPTION, versions[i].getDescription());
        }

        // bulkInsert our ContentValues array
        getActivity().getContentResolver().bulkInsert(VersionsContract.VersionEntry.CONTENT_URI, versionValuesArr);
    }

    // Attach loader to our versions database query, run when loader is initialized
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args){
        return new CursorLoader(getActivity(),
                VersionsContract.VersionEntry.CONTENT_URI,
                null,
                null,
                null,
                null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState){
        super.onViewCreated(view, savedInstanceState);
    }

    // Set the cursor in our CursorAdapter once the Cursor is loaded
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mVersionAdapter.swapCursor(data);
    }

    // reset CursorAdapter on Loader Reset
    @Override
    public void onLoaderReset(Loader<Cursor> loader){
        mVersionAdapter.swapCursor(null);
    }
}

VersionAdapter.java, the adapter for showing the version as a list in the ListView.

public class VersionAdapter extends CursorAdapter {

    public VersionAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
        mContext = context;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent){
        int layoutId = R.layout.version_item;

        View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        view.setTag(viewHolder);

        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor){
        ViewHolder viewHolder = (ViewHolder) view.getTag();

        int versionNameIndex = cursor.getColumnIndex(VersionsContract.VersionEntry.COLUMN_VERSION_NAME);
        int versionCodeIndex = cursor.getColumnIndex(VersionsContract.VersionEntry.COLUMN_VERSION_CODE);
        int apiLevelIndex = cursor.getColumnIndex(VersionsContract.VersionEntry.COLUMN_API_LEVEL);

        viewHolder.tvVersionName.setText(cursor.getString(versionNameIndex));
        viewHolder.tvVersionCode.setText(cursor.getString(versionCodeIndex));
        viewHolder.tvApiLevel.setText(cursor.getString(apiLevelIndex));
    }


    public static class ViewHolder {
        public final TextView tvVersionName, tvVersionCode, tvApiLevel;
        public ViewHolder(View view){
            tvVersionName = (TextView) view.findViewById(R.id.tv_version_name);
            tvVersionCode = (TextView) view.findViewById(R.id.tv_version_code);
            tvApiLevel = (TextView) view.findViewById(R.id.tv_api_level);
        }
    }

}

FragmentVersionDetail.java, the fragment for showing a single version information.

public class FragmentVersionDetail extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{

    private Cursor mDetailCursor;
    private int mPosition;
    private TextView tvVersionName, tvVersionCode, tvApiLevel, tvUri, tvVersionDescription;
    private Uri mUri;
    private static final int CURSOR_LOADER_ID = 0;


    public static FragmentVersionDetail newInstance(int position, Uri uri) {
        FragmentVersionDetail fragment = new FragmentVersionDetail();
        Bundle args = new Bundle();
        fragment.mPosition = position;
        fragment.mUri = uri;
        args.putInt("id", position);
        fragment.setArguments(args);
        return fragment;
    }

    public FragmentVersionDetail() {
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_version_detail, container, false);
        tvVersionName = (TextView) rootView.findViewById(R.id.tv_version_name);
        tvVersionCode = (TextView) rootView.findViewById(R.id.tv_version_code);
        tvApiLevel = (TextView) rootView.findViewById(R.id.tv_api_level);
        tvUri = (TextView) rootView.findViewById(R.id.tv_uri);
        tvVersionDescription = (TextView) rootView.findViewById(R.id.tv_version_description);
        Bundle args = this.getArguments();
        getLoaderManager().initLoader(CURSOR_LOADER_ID, args, FragmentVersionDetail.this);

        return rootView;
    }


    @Override
    public void onDetach() {
        super.onDetach();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args){
        String selection = null;
        String[] selectionArgs = null;
        if (args != null) {
            selection = VersionsContract.VersionEntry._ID;
            selectionArgs = new String[]{String.valueOf(mPosition)};
        }
        return new CursorLoader(getActivity(),
                mUri,
                null,
                selection,
                selectionArgs,
                null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState){
        super.onViewCreated(view, savedInstanceState);
    }

    // Set the cursor in our CursorAdapter once the Cursor is loaded
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mDetailCursor = data;
        mDetailCursor.moveToFirst();
        DatabaseUtils.dumpCursor(data);
        tvVersionName.setText(mDetailCursor.getString(1));
        tvVersionCode.setText(mDetailCursor.getString(2));
        tvApiLevel.setText(mDetailCursor.getString(3));
        tvVersionDescription.setText(mDetailCursor.getString(4));

        tvUri.setText(mUri.toString());
    }

    // reset CursorAdapter on Loader Reset
    @Override
    public void onLoaderReset(Loader<Cursor> loader){
        mDetailCursor = null;
    }

}

Complete example in Github

Search within Codexpedia

Custom Search

Search the entire web

Custom Search