Android Database Content Provider Example using SQLite Database

Android applications can access data directly from a SQLite database using the database helper but for other applications to get access to the same data you have to create a Content Provider. Content Providers encapsulate the data access, provide security using the Android Manifest and standardize the access via a Content URI. If the data is used only by your application there is no need to create a content provider but you can always do that in case a need that arises in the future.

The contentProvider must implement the following methods query(), insert(), update(), delete(), getType() and onCreate() to provide access to the underlying data. If you don't implement the delete method just send an exception to let the application know or if you don't get an URI match. If you have done SQLite database access using the database helper you will find there is not much difference here. All you have to do it create a provider that matches URIs and then figures out what to do with the request.

In this example we will create a simple maintenance program to maintain a list of countries. The access to the country database will be thru the Content Provider that will implement all the CRUD functions. On the screen we will display an existing list of countries and let the user add, update and delete a country. Here is the step by step process to implement the above mentioned functionality using the Content Provider.

Android Database Content Provider Example
Android Database Content Provider Insert Record
Android ListView using CursorLoader and Content Provider

Step 1: Create the SQLite database Object - CountriesDb.java

package com.as400samplecode;

import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class CountriesDb {

 public static final String KEY_ROWID = "_id";
 public static final String KEY_CODE = "code";
 public static final String KEY_NAME = "name";
 public static final String KEY_CONTINENT = "continent";

 private static final String LOG_TAG = "CountriesDb";
 public static final String SQLITE_TABLE = "Country";

 private static final String DATABASE_CREATE =
  "CREATE TABLE if not exists " + SQLITE_TABLE + " (" +
   KEY_ROWID + " integer PRIMARY KEY autoincrement," +
   KEY_CODE + "," +
   KEY_NAME + "," +
   KEY_CONTINENT + "," +
   " UNIQUE (" + KEY_CODE +"));";

 public static void onCreate(SQLiteDatabase db) {
  Log.w(LOG_TAG, DATABASE_CREATE);
  db.execSQL(DATABASE_CREATE);
 }

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

}

Step 2: Create the SQLite database Helper - MyDatabaseHelper.java

package com.as400samplecode;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyDatabaseHelper extends SQLiteOpenHelper {

 private static final String DATABASE_NAME = "TheWorld";
 private static final int DATABASE_VERSION = 1;

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

 @Override
 public void onCreate(SQLiteDatabase db) {
  CountriesDb.onCreate(db);
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  CountriesDb.onUpgrade(db, oldVersion, newVersion);
 }


}

Step 3: Create the ContentProvider - MyContentProvider.java

package com.as400samplecode;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class MyContentProvider extends ContentProvider{

 private MyDatabaseHelper dbHelper;

 private static final int ALL_COUNTRIES = 1;
 private static final int SINGLE_COUNTRY = 2;

 // authority is the symbolic name of your provider
 // To avoid conflicts with other providers, you should use 
 // Internet domain ownership (in reverse) as the basis of your provider authority. 
 private static final String AUTHORITY = "com.as400samplecode.contentprovider";

 // create content URIs from the authority by appending path to database table
 public static final Uri CONTENT_URI = 
  Uri.parse("content://" + AUTHORITY + "/countries");

 // a content URI pattern matches content URIs using wildcard characters:
 // *: Matches a string of any valid characters of any length.
    // #: Matches a string of numeric characters of any length.
 private static final UriMatcher uriMatcher;
 static {
  uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  uriMatcher.addURI(AUTHORITY, "countries", ALL_COUNTRIES);
  uriMatcher.addURI(AUTHORITY, "countries/#", SINGLE_COUNTRY);
 }

 // system calls onCreate() when it starts up the provider.
 @Override
 public boolean onCreate() {
  // get access to the database helper
  dbHelper = new MyDatabaseHelper(getContext());
  return false;
 }

 //Return the MIME type corresponding to a content URI
 @Override
 public String getType(Uri uri) {
  
  switch (uriMatcher.match(uri)) {
  case ALL_COUNTRIES: 
   return "vnd.android.cursor.dir/vnd.com.as400samplecode.contentprovider.countries";
  case SINGLE_COUNTRY: 
   return "vnd.android.cursor.item/vnd.com.as400samplecode.contentprovider.countries";
  default: 
   throw new IllegalArgumentException("Unsupported URI: " + uri);
  }
 }

 // The insert() method adds a new row to the appropriate table, using the values 
 // in the ContentValues argument. If a column name is not in the ContentValues argument, 
 // you may want to provide a default value for it either in your provider code or in 
 // your database schema. 
 @Override
 public Uri insert(Uri uri, ContentValues values) {
  
  SQLiteDatabase db = dbHelper.getWritableDatabase();
  switch (uriMatcher.match(uri)) {
  case ALL_COUNTRIES:
   //do nothing
   break;
  default:
   throw new IllegalArgumentException("Unsupported URI: " + uri);
  }
  long id = db.insert(CountriesDb.SQLITE_TABLE, null, values);
  getContext().getContentResolver().notifyChange(uri, null);
  return Uri.parse(CONTENT_URI + "/" + id);
 }

 // The query() method must return a Cursor object, or if it fails, 
 // throw an Exception. If you are using an SQLite database as your data storage, 
 // you can simply return the Cursor returned by one of the query() methods of the 
 // SQLiteDatabase class. If the query does not match any rows, you should return a 
 // Cursor instance whose getCount() method returns 0. You should return null only 
 // if an internal error occurred during the query process. 
 @Override
 public Cursor query(Uri uri, String[] projection, String selection,
   String[] selectionArgs, String sortOrder) {

  SQLiteDatabase db = dbHelper.getWritableDatabase();
  SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
  queryBuilder.setTables(CountriesDb.SQLITE_TABLE);

  switch (uriMatcher.match(uri)) {
  case ALL_COUNTRIES:
   //do nothing 
   break;
  case SINGLE_COUNTRY:
   String id = uri.getPathSegments().get(1);
   queryBuilder.appendWhere(CountriesDb.KEY_ROWID + "=" + id);
   break;
  default:
   throw new IllegalArgumentException("Unsupported URI: " + uri);
  }

  Cursor cursor = queryBuilder.query(db, projection, selection,
    selectionArgs, null, null, sortOrder);
  return cursor;

 }
 
 // The delete() method deletes rows based on the seletion or if an id is 
 // provided then it deleted a single row. The methods returns the numbers
 // of records delete from the database. If you choose not to delete the data
 // physically then just update a flag here.
 @Override
 public int delete(Uri uri, String selection, String[] selectionArgs) {

  SQLiteDatabase db = dbHelper.getWritableDatabase();
  switch (uriMatcher.match(uri)) {
  case ALL_COUNTRIES:
   //do nothing 
   break;
  case SINGLE_COUNTRY:
   String id = uri.getPathSegments().get(1);
   selection = CountriesDb.KEY_ROWID + "=" + id
   + (!TextUtils.isEmpty(selection) ? 
     " AND (" + selection + ')' : "");
   break;
  default:
   throw new IllegalArgumentException("Unsupported URI: " + uri);
  }
  int deleteCount = db.delete(CountriesDb.SQLITE_TABLE, selection, selectionArgs);
  getContext().getContentResolver().notifyChange(uri, null);
  return deleteCount;
 }

 // The update method() is same as delete() which updates multiple rows
 // based on the selection or a single row if the row id is provided. The
 // update method returns the number of updated rows.
 @Override
 public int update(Uri uri, ContentValues values, String selection,
   String[] selectionArgs) {
  SQLiteDatabase db = dbHelper.getWritableDatabase();
  switch (uriMatcher.match(uri)) {
  case ALL_COUNTRIES:
   //do nothing 
   break;
  case SINGLE_COUNTRY:
   String id = uri.getPathSegments().get(1);
   selection = CountriesDb.KEY_ROWID + "=" + id
   + (!TextUtils.isEmpty(selection) ? 
     " AND (" + selection + ')' : "");
   break;
  default:
   throw new IllegalArgumentException("Unsupported URI: " + uri);
  }
  int updateCount = db.update(CountriesDb.SQLITE_TABLE, values, selection, selectionArgs);
  getContext().getContentResolver().notifyChange(uri, null);
  return updateCount;
 }

}

Step 4: Row Layout for Country Display - country_info.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent" android:layout_height="wrap_content"
 android:orientation="vertical" android:padding="6dip">

 <TextView android:id="@+id/textView1" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_alignParentTop="true" android:text="Country:"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/textView2" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentTop="true"
  android:layout_toRightOf="@+id/textView1" android:text="("
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/code" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentTop="true"
  android:layout_toRightOf="@+id/textView2" android:text="Medium Text"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/textView3" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentTop="true"
  android:layout_toRightOf="@+id/code" android:text=")"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/name" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentTop="true"
  android:layout_toRightOf="@+id/textView3" android:text="Medium Text"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/textView4" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_below="@+id/textView1" android:text="Continent:"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/continent" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignBaseline="@+id/textView4"
  android:layout_alignBottom="@+id/textView4" android:layout_toRightOf="@+id/textView4"
  android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

Step 5: Main Layout with ListView - activity_main.xml

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 android:layout_height="match_parent">

 <Button android:id="@+id/add" style="?android:attr/buttonStyleSmall"
  android:layout_width="fill_parent" android:layout_height="wrap_content"
  android:layout_alignParentLeft="true" android:layout_alignParentTop="true"
  android:text="@string/add_country" />

 <ListView android:id="@+id/countryList" android:layout_width="match_parent"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_below="@+id/add" />


</RelativeLayout>

Step 6: Main Activity using LoaderManager for Country List - MainActivity.java

The list of countries is displayed in a ListView using the CursorLoader that queries the ContentResolver and returns a Cursor. This class implements the Loader protocol in a standard way for querying cursors, building on AsyncTaskLoader to perform the cursor query on a background thread so that it does not block the application's UI. After the loader has finished its loading just swap the new cursor from the Content Provider and return the old Cursor.
package com.as400samplecode;

import android.os.Bundle;
import android.app.Activity;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
import android.app.LoaderManager;

public class MainActivity extends Activity implements
LoaderManager.LoaderCallbacks<Cursor>{

 private SimpleCursorAdapter dataAdapter;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  displayListView(); 

  Button add = (Button) findViewById(R.id.add);
  add.setOnClickListener(new OnClickListener() {

   public void onClick(View v) {
    // starts a new Intent to add a Country
    Intent countryEdit = new Intent(getBaseContext(), CountryEdit.class);
    Bundle bundle = new Bundle();
    bundle.putString("mode", "add");
    countryEdit.putExtras(bundle);
    startActivity(countryEdit);
   }
  });

 }

 @Override
 protected void onResume() {
  super.onResume();
  //Starts a new or restarts an existing Loader in this manager
  getLoaderManager().restartLoader(0, null, this);
 }

 private void displayListView() {


  // The desired columns to be bound
  String[] columns = new String[] {
    CountriesDb.KEY_CODE,
    CountriesDb.KEY_NAME,
    CountriesDb.KEY_CONTINENT
  };

  // the XML defined views which the data will be bound to
  int[] to = new int[] { 
    R.id.code,
    R.id.name,
    R.id.continent,
  };

  // create an adapter from the SimpleCursorAdapter
  dataAdapter = new SimpleCursorAdapter(
    this, 
    R.layout.country_info, 
    null, 
    columns, 
    to,
    0);

  // get reference to the ListView
  ListView listView = (ListView) findViewById(R.id.countryList);
  // Assign adapter to ListView
  listView.setAdapter(dataAdapter);
  //Ensures a loader is initialized and active.
  getLoaderManager().initLoader(0, null, this);


  listView.setOnItemClickListener(new OnItemClickListener() {
   @Override
   public void onItemClick(AdapterView<?> listView, View view, 
     int position, long id) {
    // Get the cursor, positioned to the corresponding row in the result set
    Cursor cursor = (Cursor) listView.getItemAtPosition(position);

    // display the selected country
    String countryCode = 
     cursor.getString(cursor.getColumnIndexOrThrow(CountriesDb.KEY_CODE));
    Toast.makeText(getApplicationContext(),
      countryCode, Toast.LENGTH_SHORT).show();

    String rowId = 
     cursor.getString(cursor.getColumnIndexOrThrow(CountriesDb.KEY_ROWID));
    
    // starts a new Intent to update/delete a Country
    // pass in row Id to create the Content URI for a single row
    Intent countryEdit = new Intent(getBaseContext(), CountryEdit.class);
    Bundle bundle = new Bundle();
    bundle.putString("mode", "update");
    bundle.putString("rowId", rowId);
    countryEdit.putExtras(bundle);
    startActivity(countryEdit);

   }
  });

 }

 // This is called when a new Loader needs to be created.
 @Override
 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
  String[] projection = { 
    CountriesDb.KEY_ROWID,
    CountriesDb.KEY_CODE, 
    CountriesDb.KEY_NAME, 
    CountriesDb.KEY_CONTINENT};
  CursorLoader cursorLoader = new CursorLoader(this,
    MyContentProvider.CONTENT_URI, projection, null, null, null);
  return cursorLoader;
 }

 @Override
 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
  // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        dataAdapter.swapCursor(data);
 }

 @Override
 public void onLoaderReset(Loader<Cursor> loader) {
  // This is called when the last Cursor provided to onLoadFinished()
  // above is about to be closed.  We need to make sure we are no
  // longer using it.
  dataAdapter.swapCursor(null);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }
}


Step 7: Detail Layout for Insert,Update and Delete - detail_page.xml

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 android:layout_height="match_parent" android:paddingLeft="10dp">

 <TextView android:id="@+id/textView1" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_alignParentTop="true" android:text="@string/country_info"
  android:textAppearance="?android:attr/textAppearanceMedium"
  android:textStyle="bold" />

 <TextView android:id="@+id/choose_text" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_below="@+id/textView1" android:text="@string/choose_text"
  android:textAppearance="?android:attr/textAppearanceMedium"
  android:layout_marginTop="10dp" />

 <Spinner android:id="@+id/continentList" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_below="@+id/textView1"
  android:layout_marginTop="10dp" android:layout_toRightOf="@id/choose_text" />

 <TextView android:id="@+id/textView2" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_below="@+id/continentList" android:text="@string/country_code"
  android:textAppearance="?android:attr/textAppearanceMedium"
  android:layout_marginTop="10dp" />

 <TextView android:id="@+id/textView3" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_below="@+id/textView2" android:layout_marginTop="13dp"
  android:text="@string/country_name" android:textAppearance="?android:attr/textAppearanceMedium" />

 <EditText android:id="@+id/code" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignBottom="@+id/textView2"
  android:layout_alignLeft="@+id/name" android:layout_toRightOf="@+id/textView2"
  android:ems="3" android:maxLength="3" android:inputType="textCapCharacters"
  android:paddingLeft="10dp">

  <requestFocus />
 </EditText>

 <EditText android:id="@+id/name" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignBottom="@+id/textView3"
  android:layout_toRightOf="@+id/textView3" android:ems="30"
  android:inputType="text" android:paddingLeft="10dp" />

 <Button android:id="@+id/save" style="?android:attr/buttonStyleSmall"
  android:layout_width="wrap_content" android:layout_height="wrap_content"
  android:layout_alignParentRight="true" android:layout_below="@+id/name"
  android:layout_marginRight="21dp" android:layout_marginTop="13dp"
  android:text="@string/save_country" />

 <Button android:id="@+id/delete" style="?android:attr/buttonStyleSmall"
  android:layout_width="wrap_content" android:layout_height="wrap_content"
  android:layout_alignBaseline="@+id/save" android:layout_alignBottom="@+id/save"
  android:layout_toLeftOf="@+id/save" android:text="@string/del_country" />

</RelativeLayout>

Step 8: Detail Activity implementing CRUD operations - CountryEdit.java

package com.as400samplecode;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

public class CountryEdit extends Activity implements OnClickListener{

 private Spinner continentList;
 private Button save, delete;
 private String mode;
 private EditText code, name;
 private String id;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.detail_page);

  // get the values passed to the activity from the calling activity
  // determine the mode - add, update or delete
  if (this.getIntent().getExtras() != null){
   Bundle bundle = this.getIntent().getExtras();
   mode = bundle.getString("mode");
  }

  // get references to the buttons and attach listeners
  save = (Button) findViewById(R.id.save);
  save.setOnClickListener(this);
  delete = (Button) findViewById(R.id.delete);
  delete.setOnClickListener(this);

  code = (EditText) findViewById(R.id.code);
  name = (EditText) findViewById(R.id.name);

  
  // create a dropdown for users to select various continents
  continentList = (Spinner) findViewById(R.id.continentList);
  ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
    R.array.continent_array, android.R.layout.simple_spinner_item);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  continentList.setAdapter(adapter);

  // if in add mode disable the delete option
  if(mode.trim().equalsIgnoreCase("add")){
   delete.setEnabled(false);
  }
  // get the rowId for the specific country 
  else{
   Bundle bundle = this.getIntent().getExtras();
   id = bundle.getString("rowId");
   loadCountryInfo();
  }

 }

 public void onClick(View v) {

  // get values from the spinner and the input text fields
  String myContinent = continentList.getSelectedItem().toString();
  String myCode = code.getText().toString();
  String myName = name.getText().toString();

  // check for blanks
  if(myCode.trim().equalsIgnoreCase("")){
   Toast.makeText(getBaseContext(), "Please ENTER country code", Toast.LENGTH_LONG).show();
   return;
  }

  // check for blanks
  if(myName.trim().equalsIgnoreCase("")){
   Toast.makeText(getBaseContext(), "Please ENTER country name", Toast.LENGTH_LONG).show();
   return;
  }

  
  switch (v.getId()) {
  case R.id.save:
   ContentValues values = new ContentValues();
   values.put(CountriesDb.KEY_CODE, myCode);
   values.put(CountriesDb.KEY_NAME, myName);
   values.put(CountriesDb.KEY_CONTINENT, myContinent);
   
   // insert a record
   if(mode.trim().equalsIgnoreCase("add")){
    getContentResolver().insert(MyContentProvider.CONTENT_URI, values);
   }
   // update a record
   else {
    Uri uri = Uri.parse(MyContentProvider.CONTENT_URI + "/" + id);
    getContentResolver().update(uri, values, null, null);
   }
   finish();
   break;

  case R.id.delete:
   // delete a record
   Uri uri = Uri.parse(MyContentProvider.CONTENT_URI + "/" + id);
   getContentResolver().delete(uri, null, null);
   finish();
   break;

   // More buttons go here (if any) ...

  }
 }

 // based on the rowId get all information from the Content Provider 
 // about that country
 private void loadCountryInfo(){

  String[] projection = { 
    CountriesDb.KEY_ROWID,
    CountriesDb.KEY_CODE, 
    CountriesDb.KEY_NAME, 
    CountriesDb.KEY_CONTINENT};
  Uri uri = Uri.parse(MyContentProvider.CONTENT_URI + "/" + id);
  Cursor cursor = getContentResolver().query(uri, projection, null, null,
    null);
  if (cursor != null) {
   cursor.moveToFirst();
   String myCode = cursor.getString(cursor.getColumnIndexOrThrow(CountriesDb.KEY_CODE));
   String myName = cursor.getString(cursor.getColumnIndexOrThrow(CountriesDb.KEY_NAME));
   String myContinent = cursor.getString(cursor.getColumnIndexOrThrow(CountriesDb.KEY_CONTINENT));
   code.setText(myCode);
   name.setText(myName);
   continentList.setSelection(getIndex(continentList, myContinent));
  }


 }

 // this sets the spinner selection based on the value 
 private int getIndex(Spinner spinner, String myString){

  int index = 0;

  for (int i=0;i<spinner.getCount();i++){
   if (spinner.getItemAtPosition(i).equals(myString)){
    index = i;
   }
  }
  return index;
 }

}

Step 9: Define Strings and Arrays - strings.xml

<resources>

    <string name="app_name">SQLite Database Content Provider</string>
   <string name="menu_settings">Settings</string>
   
    <string name="add_country">Add a new Country</string>
    <string name="country_info">Country Information</string>
    <string name="country_code">Code</string>
    <string name="country_name">Name</string>
    <string name="save_country">Save</string>
    <string name="del_country">Delete</string>
    <string name="choose_text">Choose Continent</string>
    
    <string-array name="continent_array">
        <item>Africa</item>
        <item>Antartica</item>
        <item>Asia</item>
        <item>Europe</item>
        <item>North America</item>
        <item>Oceania</item>
        <item>South America</item>
    </string-array>

</resources>

Step 10: Define Android Manifest file

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.as400samplecode" android:versionCode="1"
 android:versionName="1.0">

 <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />

 <application android:icon="@drawable/ic_launcher"
  android:label="@string/app_name" android:theme="@style/AppTheme">
  <activity android:name=".MainActivity" android:label="@string/app_name">
   <intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
  </activity>
  <activity android:name=".CountryEdit" android:label="@string/app_name" />

  <provider android:name=".MyContentProvider" android:exported="true"
   android:readPermission="true" android:writePermission="true"
   android:authorities="com.as400samplecode.contentprovider" />
 </application>

</manifest>

References


23 comments :


  1. I am a newcomer to the Android world and I am facing all the difficulties associated with being a complete beginner. At this time, I find Android programming to be hard because there are many things that are not clear to me. I would like to say that I found Android Database Content Provider Example using SQLite Database to be very useful because it is written in a way that helps people learn how to program. However, being really ignorant, I have a question and I was wondering if your would like to answer it in your clear step-by-step approach to teaching.
    In the public class CountriesDb the method public static void onCreateSQLiteDatabase db) takes on an argument named db
    In the public class MyDatabaseHelper extends SQLiteOpenHelper the method
    public void onCreate(SQLiteDatabase db) takes on an argument named db
    In the public class MyContentProvider extends ContentProvider the onCreate method creates an object via new MyDatabaseHelper(getContext())
    // system calls onCreate() when it starts up the provider.
    @Override
    public boolean onCreate() {
    // get access to the database helper
    dbHelper = new MyDatabaseHelper(getContext());
    return false;
    }
    Could you please show a full example that illustrates exactly what database db references and what database will be accessed via new MyDatabaseHelper(getContext())?


    Can you show a full example that completely determines which database is referenced by db?

    ReplyDelete
    Replies
    1. The db refers to the SQLite database "TheWorld". Country Table is created when the database is first used or if your application changes the database version it will destroy the country table and recreate it again. The SQLite database is accessed using the SQLiteOpenHelper which takes care of opening the database. If you create more than one table in the same database then you can access that table also. Here is some info Saving Data in SQL Databases

      Delete
  2. Hi,

    I really like your android content provider example because it puts it all together (I found snippets to be almost useless for a beginner). It cannot be overemphasized that beginners are not experts... :)

    I have a question regarding the onCreate() method:

    // system calls onCreate() when it starts up the provider.
    @Override
    public boolean onCreate() {
    // get access to the database helper
    dbHelper = new MyDatabaseHelper(getContext());
    return false;
    }

    The comment states that the Android system calls the onCreate() method when it starts up the content provider. Furthermore, the public class MyDatabaseHelper declares

    String DATABASE_NAME = "TheWorld"; as follows:


    public class MyDatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "TheWorld";


    What is not clear to me (a beginner!) is the role of

    new MyDatabaseHelper(getContext())

    because I do not understand what

    getContext()

    does in this case.

    Does getContext() return DATABASE_NAME? What does it return?

    I look forward to reading your reply.

    Thank you very much.

    ReplyDelete
  3. Does getContext() return DATABASE_NAME? What does it return?

    No context means the scope of the current application/activity. So getContext() just returns the scope and passes the scope to the MyDatabaseHelper which extends the SQLiteOpenHelper. So when it calls the super, it actually calls the constructor for SQLiteOpenHelper which in turn creates a helper object to create, open, and/or manage a database. Read this

    SQLiteOpenHelper API Doc

    ReplyDelete
  4. Appreciate the great example. Nice to have working code to learn from. It's so hard to find good examples that actually work.

    ReplyDelete
  5. Hello,

    I was wondering if you would like to answer the following question:

    Let's assume that I want to use an outer class and an inner class in your code.

    How to modify the content provider example given in your tutorial if I want the inner class to be the SQLite OpenHelper class and the outer class to be the ContentProvider?

    Since I am new to Android,a step-by-step explanation associated with the corresponding code would be greatly appreciated.

    Thank you.

    ReplyDelete
    Replies
    1. I don't know what you mean by outer class and inner class. But basically the Content provider is like a gateway for the outside world to access the database which in this case is SQLite using URI. The SQLite helper is used by the content provider to get access to the underlying database. If you are new to android then don't worry about Content provider just read how to access Sqlite data in your app. Here is an example of query selection. Just type SQLite in the search box for more examples.

      Delete
  6. Hi,

    I am starting to learn Android programming and I found your website (a very useful one, I must say). I have a little bit of experience with Java but none at all with XML. Hence, I would like to ask a few questions regarding XML for Android.
    In mysamplecode.com you give a rather complete example of a database and a content provider. This is my question: res has several folders (drawable, values, etc). Exactly where do I put each and every xml file?

    Whoever said that ignorance is bliss must have been joking...

    Thank you.

    ReplyDelete
  7. Hi,

    I started to learn Android recently and now I am struggling with some error messages.

    To be specific, consider the following Java code from your website:


    Step 6: Main Activity using LoaderManager for Country List - MainActivity.java


    package com.as400samplecode;

    import android.os.Bundle;
    import android.app.Activity;
    import android.content.CursorLoader;
    import android.content.Intent;
    import android.content.Loader;
    import android.database.Cursor;
    import android.view.Menu;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.Button;
    import android.widget.ListView;
    import android.widget.SimpleCursorAdapter;
    import android.widget.Toast;
    import android.app.LoaderManager;

    public class MainActivity extends Activity implements
    LoaderManager.LoaderCallbacks{
    ...

    ...
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
    }
    }

    ECLIPSE reports the following error message:

    menu cannot be resolved or is not a field

    I looked at the menu folder and I could not find a file there. I would like to request your help in order to get this error fixed. So far, I cannot run this Android application in the emulator.



    ReplyDelete
    Replies
    1. Just delete that section, its not needed for this tutorial. If you create a android project it puts the code by itself so I am not sure why its not there. Anyway if you want to create it manually just put a file named activity_main.xml in the menu folder with these contents

      <menu xmlns:android="http://schemas.android.com/apk/res/android">
      <item android:id="@+id/menu_settings"
      android:title="@string/menu_settings"
      android:orderInCategory="100"
      android:showAsAction="never" />
      </menu>

      Hope it helps!

      Delete
  8. Hi,

    I am a beginner Android programmer (I started not too long ago) trying to learn the basics of SQLite databases and content providers. I have been trying to run your project using Eclipse Juno and Android 4.1. Despite my efforts (clean project, java build path, etc), the project continues to have a very stubborn error in two of the java files:

    CountryEdit.java
    R cannot be resolved to a variable

    MainActivity.java
    R cannot be resolved to a variable

    This error message appears several times in each of the above-mentioned java files. I was wondering if you would kindly tell me exactly what to do to fix this error.

    Thank you very much.

    ReplyDelete
    Replies
    1. Well those are just basic errors if you are beginner as R class is just the compiled version of your resource files such as XML, String values, etc. I would suggest you create a new project in Android and then copy the code from above. The Sqlite database and content provider are in my mind not something a beginner should approach unless you get comfortable with the various elements of your android project in the eclipse environment.

      Delete

  9. MainActivity.java
    Hi,

    I like your example because it is the first time I see a database and a content provider in the same project in full detail.
    I tried to run your code using the Android SDK in a Windows 7 computer and I got the following error message:

    MainActivity.java

    CountriesDb.KEY_CODE,
    CountriesDb.KEY_NAME,
    CountriesDb.KEY_CONTINENT
    };

    Error message:
    CountriesDb cannot be resolved to a variable (the same error appears at other locations where CountriesDb can be found in MainActivity.java).

    Please tell me how to fix it.
    Thank you very much.

    P

    ReplyDelete
  10. hi sir how can we store images in database can you give sample code

    ReplyDelete
  11. This is the first example that makes sense! Thank you very much.

    ReplyDelete
  12. Great example...and it would be nice if you could in your tutorials maybe post a link to download the project source code from your github repo or....etc

    ReplyDelete
  13. Hi there,

    I thank you for your tutorial as it is a complete example that answers several of the questions I had before I read it.

    Now, I would like to ask you a couple of questions. With respect to country code (e.g., AUS), does your code specify anywhere that the number of characters must be 3? Furthermore, does your code specify anywhere that the characters must be letters (as opposed to integers)?
    Please reply.
    Thanks!

    ReplyDelete
  14. Hello,

    I have a problem that is beyond my current skill level and I would like you to help me out.

    Assume that a user wants to see all the cities in a given country. How should the program be modified to achieve this?
    To summarize, what is needed is:

    Listing all the cities for each and every country in the database

    Accessing the cities via a 'search for cities in the chosen country'


    Could you please post the appropriate java code and XML markup?

    Thank you very much for your help.

    ReplyDelete
  15. Thank you so much for this example! I'm learning how to move from the deprecated SimpleCursorAdapter to CursorLoader/ContentProvider (which seems like a a hideously complicated way of doing the same thing, though needed if I'm going to connect to Google Docs.) My app is slightly more complicated in that it has an App class at the top, but with the help of your clear organization and comments, I seem to have figured out where to put everything. Your example worked perfectly with the exception of calls to finish().

    ReplyDelete
  16. I've literally searched the entire internet and this is the BEST and ONLY example that shows how to implement CursorLoader/ContnetProvider! Thank you SOO much for putting together this tutorial.

    ReplyDelete
  17. Please add this statement after delete is called:
    getLoaderManager().restartLoader(0, null, this);

    This will force the ListView to reload.

    ReplyDelete
  18. How could I complish if I had three activities that were writable...(Ex. Add A New Country, Add A New State, Add A New Planet). Each activity must have it's own list of items and items cannot show up in each others list. The database must store all entries. How can I accomplish this?

    ReplyDelete