Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
25 views57 pages

ANDROID

The document discusses different ways to persist data in an Android application including internal storage, external storage, shared preferences, and databases. It provides details on each method and examples of how to use shared preferences and the onCreate lifecycle method.

Uploaded by

ds1048
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views57 pages

ANDROID

The document discusses different ways to persist data in an Android application including internal storage, external storage, shared preferences, and databases. It provides details on each method and examples of how to use shared preferences and the onCreate lifecycle method.

Uploaded by

ds1048
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 57

UNIT 3:Data Persistence


Android Storage System is the name given to these storage systems. Internal
storage, external storage, shared preferences, database, and shared storage are
some of the storage options offered by Android.
For Android, there are primarily three basic ways of persisting data:
A lightweight mechanism known as shared preferences to save small chunks of
data
➤ Traditional file systems
➤ A relational database management system through the support of
SQLite databases

1. Storage on the Inside

When you install an app on your phone, the Android operating system will give
you some form of secret internal storage where the app can store its private data.
No other application has access to this information. When you uninstall an
application, all of the data associated with it is also removed.
To save a file to the internal storage, you must first obtain it from the internal
directory. You can do this by calling the getFilesDir() or getCacheDir() methods.
The getFilesDir() method returns the absolute path to the directory where files are
created on the filesystem. getCacheDir() returns the absolute path to the
filesystem’s application-specific cache directory.

2. External Hard Drives

Most Android devices have relatively low internal storage. As a result, we keep
our data on an external storage device. These storage units are accessible to
everyone, which means they can be accessed by all of your device’s applications.
You can also access the storage by connecting your mobile device to a computer.
You must obtain the READ EXTERNAL STORAGE permission from the user in
order to gain access to the external storage. As a result, any application with this
permission has access to your app’s data.

3. Using the Shared Preferences


You can use the shared preferences if you only have a little amount of data to
keep and don’t want to use the internal storage. Shared Preferences are used to
store data in a key-value format, which means you’ll have one key and the
associated data or value will be stored depending on that key. The data saved in
the shared preferences will remain with the application until you delete it from
your phone. All shared preferences will be deleted from the device if you
uninstall the application.

4. Using Android Database

Databases are collections of data that are organized and saved for future use.
Using a Database Management System, you can store any type of data in your
database. All you have to do is establish the database and use one query to
perform all of the operations, such as insertion, deletion, and searching. The
query will be passed to the database, which will return the desired output. In
Android, an SQLite database is an example of a database.

onCreate(Bundle savedInstanceState) Activity Function And Example In Android


onCreate(Bundle savedInstanceState) Function in Android:

 When an Activity first call or launched then onCreate(Bundle savedInstanceState)


method is responsible to create the activity.
 When ever orientation(i.e. from horizontal to vertical or vertical to horizontal) of
activity gets changed or when an Activity gets forcefully terminated by any
Operating System then savedInstanceState i.e. object of Bundle Class will save the
state of an Activity.
 After Orientation changed then onCreate(Bundle savedInstanceState) will call and
recreate the activity and load all data from savedInstanceState.
 Basically Bundle class is used to stored the data of activity whenever above
condition occur in app.
 onCreate() is not required for apps. But the reason it is used in app is because that
method is the best place to put initialization code.
 You could also put your initialization code in onStart() or onResume() and when you
app will load first, it will work same as in onCreate().

Example of onCreate(Bundle savedInstanceState):


Lets create a simple program to understand onCreate(Bundle savedInstanceState)


more deeply.
In the below example we will ask the user to Enter the name, save his name in
Bundle object and then display the same name when user rotates the screen.
First create a new project, name it Exampleoncreate and create a activity name
MainActivity. Also create a content_main.xml in layout if not present by default.
For designing UI we will create 3 things in content_main.xml:

 We have used Linear Layout


 a TextView to display basic detail about the program to user,
 EditText where user will enter the name which will be save in Bundle object
 a TextView which will display the name on screen rotation

Below is the complete code of content_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Fill Name Then Rotate:"
android:id="@+id/textView2" />
<!-- used to enter the data and get saved in bundle-->
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
/>

<!-- retrived data shown in this textview -->


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

Now go to MainActivity.java and paste the below code. The explanation is


given using comments in the code itself.

public class MainActivity extends AppCompatActivity {


private static String TAG = "ActivityName";
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
}
//Saving The Text Entered by User
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState");
final EditText editText=
(EditText) findViewById(R.id.editText);// getting the reference of editext
from xml
CharSequence text = editText.getText();// getting text u entered in edittext
outState.putCharSequence("savedText", text);// saved that text in bundle
object i.e. outState

}
//Restoring the State
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, "onRestoreInstanceState");
final TextView textView =
(TextView) findViewById(R.id.textView);// getting the reference of
textview from xml
CharSequence savedText=
savedInstanceState.getCharSequence("savedText");// getting the text of
editext

textView.setText(savedText);// set the text that is retrieved from bundle object


}
}

Output:
Now run the application in Emulator and enter the name. In our case we have
entered AbhiAndroid.

Now rotate the screen from vertical to horizontal (Press ctrl + F12 in Windows &
Fn+Left CTRL+F12 on Mac to rotate). After you change screen orientation you
will see the same name appear just right side of name.

So we have retrieved the data from Bundle object.

Shared Preference Tutorial With Example In Android Studio


Shared Preference in Android are used to save data based on key-value pair. If we
go deep into understanding of word: shared means to distribute data within and
preference means something important or preferable, so SharedPreferences data is
shared and preferred data.
Shared Preference can be used to save primitive data type: string, long, int, float
and Boolean.
What Is Preference File?
Before we begin explaining shared preference, it is important to understand
preference file.
A preference file is actually a xml file saved in internal memory of device. Every
application have some data stored in memory in a directory data/data/application
package name i.e data/data/com.abhiandroid.abhiapp so whenever
getSharedPreferences(String name,int mode) function get called it validates the file
that if it exists or it doesn’t then a new xml is created with passed name.
Two Ways To Save Data Through Shared Preference:
There are two different ways to save data in Android through Shared Preferences –
One is using Activity based preferences and other is creating custom preferences.
Activity Preferences:

 For activity preferences developer have to call function getPreferences (int


mode) available in Activity class
 Use only when one preference file is needed in Activity
 It doesn’t require name as it will be the only preference file for this activity
 Developer doesn’t usually prefer using this even if they need only one preference file
in Activity. They prefer using custom getSharedPreferences(String name,int
mode).

To use activity preferences developer have to call function getPreferences (int


mode) available in Activity class. The function getPreferences(int mode) call the
other function used to create custom preferences i.e getSharedPreferences(String
name,int mode). Just because Activity contains only one preference file so
getPreferences(int mode) function simply pass the name of Activity class to create
a preference file.
Custom Preferences:

 Developer needs to use getSharedPreferences(String name,int mode) for custom


preferences
 Used in cases when more than one preference file required in Activity
 Name of the preference file is passed in first parameter

Custom Preferences can be created by calling


function getSharedPreferences(String name,int mode), it can be called from
anywhere in the application with reference of Context. Here name is any preferred
name for example: User,Book etc. and mode is used to set kind of privacy for file.
Mode And Its Type In Shared Preference:
To create activity based preferences or custom preferences developer have to pass
mode to let the system know about privacy of preference file.
Before we begin a brief discussion on Context
Context is an abstract class used to get global information in Android Application
resources like strings, values, drawables, assets etc. Here modes are declared and
static final constant in Context class.
There are three types of Mode in Shared Preference:

1.Context.MODE_PRIVATE – default value (Not accessible outside of your


application)
2.Context.MODE_WORLD_READABLE – readable to other apps

3.Context.MODE_WORLD_WRITEABLE – read/write to other apps

MODE_PRIVATE – It is a default mode. MODE_PRIVATE means that when


any preference file is created with private mode then it will not be accessible
outside of your application. This is the most common mode which is used.
MODE_WORLD_READABLE – If developer creates a shared preference file
using mode world readable then it can be read by anyone who knows it’s name, so
any other outside application can easily read data of your app. This mode is very
rarely used in App.
MODE_WORLD_WRITEABLE – It’s similar to mode world readable but with
both kind of accesses i.e read and write. This mode is never used in App by
Developer.
Creating Shared Preference:
As discussed above, to create shared preference developer needs to
call getPreferences(int mode) or getSharedPreferences(String name,int
mode) method. Both of the function return SharedPreferences object to deal with
save and get values from preference file.
SharedPreferences is a singleton class means this class will only have a single
object throughout the application lifecycle. However there can multiple preference
files so all the preference files can be read and write using SharedPreferences class.

Save Data In SharedPreferences:


Step 1:
To save data in SharedPreferences developer need to called edit() function of
SharedPreferences class which returns Editor class object.
Step 2:
Editor class provide different function to save primitive time data. For example,
Editor have all primitive data function including String type data as putInt(String
keyName,int value).
Common primitive function format is: put + Primitive Type name (String
keyname, Primitive Type value)
Step 3:

Now make sure that key name should be unique for any values you save otherwise
it will be overrided. To exactly save the values you put in different primitive
functions you must call commit() function of Editor.

Read or get data from SharedPreferences


Step 1: To read data first developer have to get reference of SharedPreferences
object by calling getPreferences (int mode) or getSharedPreferences (String
name,int mode).
Step 2: Then developer can get values using SharedPreferences object by calling
different primitive type function starting with get+Primitive Type name. Here
every function has an additional parameter for every Primitive Type as default
value in case there is no value found corresponding to passed key.
For example, to get saved int value just call getInt(“GameScore”,-1) function
where “GameScore” is key and -1 is a default value in case no value was saved for
GameScore.

Remove Data Or Clear All Data


Similar to save data there is a function remove(String
key) in SharedPreferences.Editor to remove a particular key based data from
preference file
To clear all data just call function clear() available in SharedPreferences.Editor.
Make sure to call commit() method to save changes. So clear data will never delete
the preference file but make it empty.

Code Of Saving & Retrieving Data in Shared Preference:

public static final String PREFS_GAME ="com.abhiandroid.abhiapp.GamePlay";


public static final String GAME_SCORE= "GameScore";

//======== Code to save data ===================


SharedPreferences sp = getSharedPreferences(PREFS_GAME
,Context.MODE_PRIVATE);
sp.edit().putInt(GAME_SCORE,100).commit();

//========= Code to get saved/ retrieve data ==============

SharedPreferences sp = getSharedPreferences(PREFS_GAME
,Context.MODE_PRIVATE);
int sc = sp.getInt(GAME_SCORE,0);
Log.d("AbhiAndroid","achieved score is "+ sc);

Shared Preference Example:


Let’s use Shared Preference for a very basic purpose of saving login details of a
person i.e. email and password on his device. So he don’t have to re-enter his login
details every time he opens the App.

Step 1: Create a new project and create an login Activity activity_login.xml. In this
create a login UI asking user email and password with an option of remember
me checkbox. Also a button displaying Signin or Register.
Below is the complete code login UI activity_login.xml

<LinearLayout 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:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.samplesharedpreferences.LoginActivity">

<!-- Login progress -->


<ProgressBar
android:id="@+id/login_progress"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"/>

<ScrollView
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:id="@+id/email_login_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"

android:layout_height="wrap_content">

<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_email"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true"/>

</android.support.design.widget.TextInputLayout>

<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_password"
android:imeActionId="@+id/login"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionUnspecified"
android:inputType="textPassword"

android:maxLines="1"
android:singleLine="true"/>

</android.support.design.widget.TextInputLayout>

<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Remember Me"
android:id="@+id/checkBoxRememberMe"/>

<Button
android:id="@+id/email_sign_in_button"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/action_sign_in"
android:textStyle="bold"/>

</LinearLayout>
</ScrollView>
</LinearLayout>

Step 2: Now lets code the LoginActivity.java where we will create a Login form.
Below is the complete code of LoginActivity.java with explanation included in
comment.

public class LoginActivity extends AppCompatActivity {

// UI references.
private EditText mEmailView;
private EditText mPasswordView;

private CheckBox checkBoxRememberMe;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Set up the login form.
mEmailView = (EditText) findViewById(R.id.email);
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView.setOnEditorActionListener(new
TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent
keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin();
return true;
}
return false;
}

});

Button mEmailSignInButton = (Button)


findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});

checkBoxRememberMe = (CheckBox)
findViewById(R.id.checkBoxRememberMe);
//Here we will validate saved preferences
if (!new PrefManager(this).isUserLogedOut()) {
//user's email and password both are saved in preferences
startHomeActivity();
}

/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/

private void attemptLogin() {

// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);

// Store values at the time of the login attempt.


String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();

boolean cancel = false;


View focusView = null;

// Check for a valid password, if the user entered one.


if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}

// Check for a valid email address.


if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {

mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}

if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// save data in local shared preferences
if (checkBoxRememberMe.isChecked())
saveLoginDetails(email, password);
startHomeActivity();
}
}

private void startHomeActivity() {


Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}

private void saveLoginDetails(String email, String password) {


new PrefManager(this).saveLoginDetails(email, password);
}

private boolean isEmailValid(String email) {


//TODO: Replace this with your own logic
return email.contains("@");
}

private boolean isPasswordValid(String password) {


//TODO: Replace this with your own logic
return password.length() > 4;
}
}

Step 3: Create a new java class for Shared Preference PrefManager. Below is the
code of PrefManager.java

public class PrefManager {


Context context;
PrefManager(Context context) {
this.context = context;
}
public void saveLoginDetails(String email, String password) {
SharedPreferences sharedPreferences =
context.getSharedPreferences("LoginDetails", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Email", email);
editor.putString("Password", password);
editor.commit();

public String getEmail() {


SharedPreferences sharedPreferences =
context.getSharedPreferences("LoginDetails", Context.MODE_PRIVATE);
return sharedPreferences.getString("Email", "");
}

public boolean isUserLogedOut() {


SharedPreferences sharedPreferences =
context.getSharedPreferences("LoginDetails", Context.MODE_PRIVATE);
boolean isEmailEmpty = sharedPreferences.getString("Email", "").isEmpty();
boolean isPasswordEmpty = sharedPreferences.getString("Password",
"").isEmpty();
return isEmailEmpty || isPasswordEmpty;
}
}

Step 4: Design the simple Home UI where we will display Welcome message
along with his saved email address in Shared preference. Below is the code of
content_home.xml

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.samplesharedpreferences.HomeActivity"
tools:showIn="@layout/activity_home">

<TextView
android:id="@+id/textViewUser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="Welcome"/>

</RelativeLayout>

Output:
Step 1: Now run the App in Emulator. You will see the below output screen

Step 2: Fill the email, password and click on remember me checkbox. In our case
we have filled [email protected] and abhiandroid.

Step 3: It will display you the welcome message along with your email ID. Now
close the App and reopen it. You will see the same message of Welcome and your
email address printed. So here we have stored your email address on your device
itself using Shared Preference.

Internal Storage In Android Studio


In this tutorial we are going to learn about internal storage of data/files in Android
App using example or you can say the primary memory of your phone. It is also
important to note that the data is stored in a file specified by the user but user can
not access that file, also this file can only be accessed by the application itself.

Important Points About Internal Storage In Android:

 The stored data in memory is allowed to read and write files.


 When files are stored in internal storage these file can only be accessed by the
application itself not by other applications.
 These files in storage exist till the application stays over the device, as you uninstall
associated files get removed automatically.
 The files are stored in directory data/data which is followed by the application
package name.
 User can explicitly grant the permission to other apps to access files.

 To make the data private i.e you can use MODE_PRIVATE as discussed below
about the modes.
 Technique is best suited when data can only be access by the application neither by
the user nor by other apps.
 The data is stored in a file which contains data in bit format so it’s required to
convert data before adding it to a file or before extracting from a file.

Modes of Internal Storage


MODE_PRIVATE — In private mode the data stored earlier is always overridden
by the current data i.e every time you try to commit a new write to a file which
removes or override the previous content. We have used MODE_PRIVATE in the
example at the end of this article.
MODE_APPEND — In this mode the data is append to the existing content i.e
keep adding data.

Write data to file in Internal Storage:

 Define the filename as string and also define data you wanna write to file as string or
in any format generated from app or any other source.
 Use FileOutputStream method by creating its object as defined.
 Convert data into byte stream before writing over file because file accepts only byte
format further close the file using file object.

String File_Name= "Demo.txt"; //gives file name


String Data="Hello!!"; //define data

FileOutputStream fileobj = openFileOutput( File_Name,


Context.MODE_PRIVATE);
byte[] ByteArray = Data.getBytes(); //Converts into bytes stream
fileobj.write(ByteArray); //writing to file
fileobj.close(); //File closed

External Storage In Android Studio


In this tutorial we gonna study about storage of data/files in android external
storage or you can say the secondary memory/SD card of your phone. The data is
stored in a file specified by the user itself and user can access these file. These files
are only accessible till the application exits or you have SD card mounted on your
device.

Important Note: It is necessary to add external storage the permission to read and
write. For that you need to add permission in android Manifest file.
Open AndroidManifest.xml file and add permissions to it just after the package
name.

<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />

External Storage Availability In Android Studio


To avoid crashing app it is required to check before whether storage SD card is
available for read & write operations. getExternalStorageState() method is used to
determine the state of the storage media i.e SD card is mounted, is it readable , it is
writable etc.. all this kind of information.

boolean isAvailable= false;


boolean isWritable= false;
boolean isReadable= false;
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
// Read and write operation possible
isAvailable= true;
isWritable= true;
isReadable= true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)){
// Read operation possible
isAvailable= true;
isWritable= false;
isReadable= true;

} else {
// SD card not mounted
isAvailable = false;
isWritable= false;
isReadable= false; }

Methods to Store Data In Android:

 getExternalStorageDirectory() – Older way to access external storage in API


Level less than 7. It is absolute now and not recommended. It directly get the
reference to the root directory of your external storage or SD Card.
 getExternalFilesDir(String type) – It is recommended way to enable us to create
private files specific to app and files are removed as app is uninstalled. Example is
app private data.
 getExternalStoragePublicDirectory() : This is current recommended way that
enable us to keep files public and are not deleted with the app uninstallation.
Example images clicked by the camera exists even we uninstall the camera app.

Content Providers in Android

A content provider is a library in Android that manages access to a central


repository of data. Content providers are part of the Android Framework, which
provides user interface (UI) features.

Content providers can:

 Help applications manage access to data


 Share data with other apps
 Offer granular control over the permissions for accessing data
 Allow other applications to securely access or modify data

.

A content provider behaves very much like a database — you can query it, edit its
content, as well as add or delete its content. However, unlike a database, a content
provider can use different ways to store its data. The data can be stored in a
database, in files, or even over a network.

Android ships with many useful content providers, including the following:

 Browser — Stores data such as browser bookmarks, browser history, and so on


 CallLog — Stores data such as missed calls, call details, and so on
 Contacts — Stores contact details
 MediaStore — Stores media files such as audio, video and images
 Settings — Stores the device’s settings and preferences

Content URI

Content URI(Uniform Resource Identifier) is the key concept of Content


providers. To access the data from a content provider, URI is used as a query
string.

Structure of a Content URI: content://authority/optionalPath/optionalID

Details of different parts of Content URI:

 content:// – Mandatory part of the URI as it represents that the given URI is a
Content URI.
 authority – Signifies the name of the content provider like contacts, browser, etc.
This part must be unique for every content provider.
 optionalPath – Specifies the type of data provided by the content provider. It is
essential as this part helps content providers to support different types of data that
are not related to each other like audio and video files.
 optionalID – It is a numeric value that is used when there is a need to access a
particular record.

If an ID is mentioned in a URI then it is an id-based URI otherwise a


directory-based URI.

Operations in Content Provider

Four fundamental operations are possible in Content Provider namely Create,


Read, Update, and Delete. These operations are often termed as CRUD
operations.

 Create: Operation to create data in a content provider.


 Read: Used to fetch data from a content provider.
 Update: To modify existing data.
 Delete: To remove existing data from the storage.

Working of the Content Provider

UI components of android applications like Activity and Fragments use an object


CursorLoader to send query requests to ContentResolver. The ContentResolver
object sends requests (like create, read, update, and delete) to the ContentProvider
as a client. After receiving a request, ContentProvider process it and returns the
desired result. Below is a diagram to represent these processes in pictorial form.

Create Content Provider

This involves number of simple steps to create your own content provider.

 First of all you need to create a Content Provider class that extends the
ContentProviderbaseclass.
 Second, you need to define your content provider URI address which will be used to
access the content.
 Next you will need to create your own database to keep the content. Usually,
Android uses SQLite database and framework needs to override onCreate() method
which will use SQLite Open Helper method to create or open the provider's
database. When your application is launched, the onCreate() handler of each of its
Content Providers is called on the main application thread.
 Next you will have to implement Content Provider queries to perform different
database specific operations.
 Finally register your Content Provider in your activity file using <provider> tag.

Following are the six abstract methods and their description which are
essential to override as the part of ContenProvider class:

Abstract
Description
Method
A method that accepts arguments and fetches the data from the
query()
desired table. Data is retired as a cursor object.
To insert a new row in the database of the content provider.
insert()
It returns the content URI of the inserted row.
This method is used to update the fields of an existing row.
update()
It returns the number of rows updated.
This method is used to delete the existing rows.
delete()
It returns the number of rows deleted.
This method returns the Multipurpose Internet Mail
Extension(MIME)
getType()
type of data to the given Content URI.
As the content provider is created, the android system calls
onCreate()

Abstract
Description
Method
this method immediately to initialise the provider.

Accessing a provider

An application accesses the data from a content provider with a ContentResolver


client object. This object has methods that call identically-named methods in the
provider object, an instance of one of the concrete subclasses of ContentProvider.
The ContentResolver methods provide the basic "CRUD" (create, retrieve, update,
and delete) functions of persistent storage.

The ContentResolver object in the client application's process and the


ContentProvider object in the application that owns the provider automatically
handle inter-process communication. ContentProvider also acts as an abstraction
layer between its repository of data and the external appearance of data as tables.

Note: To access a provider, your application usually has to request specific


permissions in its manifest file. This is described in more detail in the section
Content Provider Permissions

For example, to get a list of the words and their locales from the User Dictionary
Provider, you call ContentResolver.query(). The query() method calls the
ContentProvider.query() method defined by the User Dictionary Provider. The
following lines of code show a ContentResolver.query() call:

// Queries the user dictionary and returns results


mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
mProjection, // The columns to return for each row
mSelectionClause // Selection criteria
mSelectionArgs, // Selection criteria
mSortOrder); // The sort order for the returned rows

Table 2 shows how the arguments to


query(Uri,projection,selection,selectionArgs,sortOrder) match an SQL SELECT
statement:

Table 2: Query() compared to SQL query.

query() SELECT
Notes
argument keyword/parameter
Uri maps to the table in the
Uri FROM table_name provider named
table_name.
projection is an array of
columns that should be
projection col,col,col,...
included for each row
retrieved.
selection specifies the
selection WHERE col = value
criteria for selecting rows.
(No exact
equivalent. Selection
selectionArgsarguments replace ?
placeholders in the
selection clause.)
sortOrder specifies the
ORDER BY
sortOrder order in which rows appear
col,col,...
in the returned Cursor.

Making the query

To query a content provider, you can use either the ContentResolver.query()


method or the Activity.managedQuery() method. Both methods take the same set
of arguments, and both return a Cursor object. However, managedQuery() causes
the activity to manage the life cycle of the Cursor. A managed Cursor handles all of
the niceties, such as unloading itself when the activity pauses, and requerying itself
when the activity restarts. You can ask an Activity to begin managing an
unmanaged Cursor object for you by calling Activity.startManagingCursor().

The first argument to either query() or managedQuery() is the provider URI — the
CONTENT_URI constant that identifies a particular ContentProvider and data set
(see URIs earlier).

To restrict a query to just one record, you can append the _ID value for that record
to the URI — that is, place a string matching the ID as the last segment of the path
part of the URI. For example, if the ID is 23, the URI would be:

content://. . . ./23

There are some helper methods, particularly ContentUris.withAppendedId() and


Uri.withAppendedPath(), that make it easy to append an ID to a URI. Both are
static methods that return a Uri object with the ID added. So, for example, if you
were looking for record 23 in the database of people contacts, you might construct
a query as follows:

import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;

// Use the ContentUris method to produce the base URI for the contact with _ID ==
23.
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);

// Alternatively, use the Uri method to produce the base URI.


// It takes a string rather than an integer.
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");

// Then query for this specific record:


Cursor cur = managedQuery(myPerson, null, null, null, null);

The other arguments to the query() and managedQuery() methods delimit the query
in more detail. They are:

 The names of the data columns that should be returned. A null value returns all
columns. Otherwise, only columns that are listed by name are returned. All the
content providers that come with the platform define constants for their columns.
For example, the android.provider.Contacts.Phones class defines constants for the
names of the columns in the phone table illustrated earlier &mdash _ID,
NUMBER, NUMBER_KEY, NAME, and so on.
 A filter detailing which rows to return, formatted as an SQL WHERE clause
(excluding the WHERE itself). A null value returns all rows (unless the URI limits
the query to a single record).
 Selection arguments.
 A sorting order for the rows that are returned, formatted as an SQL ORDER BY
clause (excluding the ORDER BY itself). A null value returns the records in the
default order for the table, which may be unordered.

Let's look at an example query to retrieve a list of contact names and their primary
phone numbers:

import android.provider.Contacts.People;
import android.database.Cursor;

// Form an array specifying which columns to return.


String[] projection = new String[] {
People._ID,
People._COUNT,
People.NAME,
People.NUMBER
};

// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;

// Make the query.


Cursor managedCursor = managedQuery(contacts,

projection, // Which columns to return


null, // Which rows to return (all rows)
null, // Selection arguments (none)
// Put the results in ascending order by name
People.NAME + " ASC");

This query retrieves data from the People table of the Contacts content provider. It
gets the name, primary phone number, and unique record ID for each contact. It
also reports the number of records that are returned as the _COUNT field of each
record.

The constants for the names of the columns are defined in various interfaces —
_ID and _COUNT in BaseColumns, NAME in PeopleColumns, and NUMBER in
PhoneColumns. The Contacts.People class implements each of these interfaces,
which is why the code example above could refer to them using just the class
name.

Using Static Resources


Besides creating and using files dynamically during run time, it is also possible to add files to
your package during design time so that you can use it during run time. For example, you
may want to bundle some help files with your package so that you can display some help
messages when users need it. In this case, you can add the files to the res/raw folder (you need
to create this folder yourself) of your pack- age. Figure 6-8 shows the res/raw folder containing a
file named textfile.txt.
To make use of the file in code, use the getResources() method to return a Resources object
and then use its openRawResource() method to open the file contained in the res/raw folder:
import java
import java.io.BufferedReader;
/** Called when the activity is first created. */@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

FIGURE 6-8

InputStream is = this.getResources().openRawResource(R.raw.textfile);
BufferedReader br = new BufferedReader(new InputStreamReader(is)); String
str = null;
try {
while ((str = br.readLine()) != null) {Toast.makeText(getBaseContext(),
str, Toast.LENGTH_SHORT).show();
}
is.close();
br.close();
} catch (IOException e) {e.printStackTrace();
}

textBox = (EditText) findViewById(R.id.txtText1); Button saveBtn = (Button)


findViewById(R.id.btnSave);Button loadBtn = (Button) findViewById(R.id.btnLoad);

saveBtn.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {


}
});

loadBtn.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {


}
});
}

The resource ID of the resource stored in the res/raw folder is named after its
filename without its extension. For example, if the text file is textfile.txt, then its
resource ID is R.raw.textfile.

CREATING AND USING DATABASES


So far, all the techniques you have seen are useful for saving simple sets of data.
For saving relational data, using a database is much more efficient. For example,
if you want to store the results of all the students in a school, it is much more
efficient to use a database to represent them because you can use database querying
to retrieve the results of the specific students. Moreover, using databases enables
you to enforce data integrity by specifying the relationships between different sets
of data.
Android uses the SQLite database system. The database that you create for an
application is only acces- sible to itself; other applications will not be able to
access it.
In this section, you will learn how to programmatically create a SQLite database in
your Android application. For Android, the SQLite database that you create
programmatically in an application is always stored in the
/data/data/<package_name>/databases folder.

Creating the DBAdapter Helper Class


A good practice for dealing with databases is to create a helper class to
encapsulate all the complexi- ties of accessing the data so that it is transparent to
the calling code. Hence, for this section, you will create a helper class called
DBAdapter that creates, opens, closes,
and uses a SQLite database.
In this example, you are going to create a database named MyDB contain- ing one
table named contacts. This table will have three columns: _id,
name, and email (see Figure 6-9). FIGURE 6-9

Creating the Database Helper Class



1. Using Eclipse, create an Android project and name it Databases.


2. Add a new class file to the project and name it DBAdapter.java (see Figure 6-10).
3. Add the following statements in bold to the DBAdapter.java file:
package net.learn2develop.Databases;

import android.content.ContentValues; import android.content.Context;

FIGURE 6-10

import android.database.Cursor; import android.database.SQLException;


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

public class DBAdapter {


public static final String KEY_ROWID = “_id”; public static final String
KEY_NAME = “name”; public static final String KEY_EMAIL = “email”; private
static final String TAG = “DBAdapter”;

private static final String DATABASE_NAME = “MyDB”; private static final


String DATABASE_TABLE = “contacts”; private static final int
DATABASE_VERSION = 1;

private static final String DATABASE_CREATE =


“create table contacts (_id integer primary key autoincrement, “
+ “name text not null, email text not null);”;private final Context context;
private DatabaseHelper DBHelper; private SQLiteDatabase db;

public DBAdapter(Context ctx)


{
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}

private static class DatabaseHelper extends SQLiteOpenHelper


{

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

@Override
public void onCreate(SQLiteDatabase db)
{
try {
db.execSQL(DATABASE_CREATE);
} catch (SQLException e) {e.printStackTrace();
}
}

@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 contacts”);

onCreate(db);
}
}

//---opens the database---


public DBAdapter open() throws SQLException
{
db = DBHelper.getWritableDatabase(); return this;
}

//---closes the database---public void close()


{
DBHelper.close();
}

//---insert a contact into the database---


public long insertContact(String name, String email)
{
ContentValues initialValues = new ContentValues();

initialValues.put(KEY_NAME, name); initialValues.put(KEY_EMAIL,


email);
return db.insert(DATABASE_TABLE, null, initialValues);
}

//---deletes a particular contact--- public boolean deleteContact(long rowId)


{
return db.delete(DATABASE_TABLE, KEY_ROWID + “=” + rowId, null) > 0;
}

//---retrieves all the contacts--- public Cursor getAllContacts()


{
return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
KEY_EMAIL}, null, null, null, null, null);
}

//---retrieves a particular contact---


public Cursor getContact(long rowId) throws SQLException
{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
KEY_EMAIL}, KEY_ROWID + “=” + rowId, null,
null, null, null, null);if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}

//---updates a contact---
public boolean updateContact(long rowId, String name, String email)

{
ContentValues args = new ContentValues(); args.put(KEY_NAME, name);
args.put(KEY_EMAIL, email);
return db.update(DATABASE_TABLE, args, KEY_ROWID + “=” + rowId, null) >
0;
}
}

How It Works
You first defined several constants to contain the various fields for the table that
you are going to create in your database:
public static final String KEY_ROWID = “_id”; public static final String
KEY_NAME = “name”; public static final String KEY_EMAIL = “email”; private
static final String TAG = “DBAdapter”;

private static final String DATABASE_NAME = “MyDB”; private static final


String DATABASE_TABLE = “contacts”; private static final int
DATABASE_VERSION = 1;

private static final String DATABASE_CREATE =


“create table contacts (_id integer primary key autoincrement, “
+ “name text not null, email text not null);”;

In particular, the DATABASE_CREATE constant contains the SQL statement for


creating the contacts table within the MyDB database.
Within the DBAdapter class, you also extend the SQLiteOpenHelper class, which is a
helper class in Android to manage database creation and version management. In
particular, you override the onCreate() and onUpgrade() methods:
public class DBAdapter {
public static final String KEY_ROWID = “_id”; public static final String KEY_NAME
= “name”; public static final String KEY_EMAIL = “email”; private static final String
TAG = “DBAdapter”;

private static final String DATABASE_NAME = “MyDB”; private static final String
DATABASE_TABLE = “contacts”;private static final int DATABASE_VERSION = 1;

private static final String DATABASE_CREATE =


“create table contacts (_id integer primary key autoincrement, “
+ “name text not null, email text not null);”;private final Context context;
private DatabaseHelper DBHelper; private SQLiteDatabase db;

public DBAdapter(Context ctx)


{

this.context = ctx;
DBHelper = new DatabaseHelper(context);
}

private static class DatabaseHelper extends SQLiteOpenHelper


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

@Override
public void onCreate(SQLiteDatabase db)
{
try {
db.execSQL(DATABASE_CREATE);
} catch (SQLException e) {e.printStackTrace();
}
}

@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 contacts”);
onCreate(db);
}
}

The onCreate() method creates a new database if the required database is not
present. The onUpgrade() method is called when the database needs to be upgraded.
This is achieved by checking the value defined in the DATABASE_VERSION
constant. For this implementation of the onUpgrade() method, you simply drop the
table and create it again.
You can then define the various methods for opening and closing the database, as
well as the methods for adding/editing/deleting rows in the table:
public class DBAdapter {
//...
//...

//---opens the database---


public DBAdapter open() throws SQLException

{
db = DBHelper.getWritableDatabase(); return this;
}

//---closes the database---public void close()

DBHelper.close();
}

//---insert a contact into the database---


public long insertContact(String name, String email)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name); initialValues.put(KEY_EMAIL,
email);
return db.insert(DATABASE_TABLE, null, initialValues);
}

//---deletes a particular contact--- public boolean deleteContact(long rowId)


{
return db.delete(DATABASE_TABLE, KEY_ROWID + “=” + rowId, null) > 0;
}

//---retrieves all the contacts--- public Cursor getAllContacts()


{

return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,


KEY_EMAIL}, null, null, null, null, null);
}

//---retrieves a particular contact---


public Cursor getContact(long rowId) throws SQLException
{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
KEY_EMAIL}, KEY_ROWID + “=” + rowId, null,
null, null, null, null);if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;

//---updates a contact---
public boolean updateContact(long rowId, String name, String email)
{
ContentValues args = new ContentValues(); args.put(KEY_NAME, name);
args.put(KEY_EMAIL, email);
return db.update(DATABASE_TABLE, args, KEY_ROWID + “=” + rowId, null) >
0;
}
}

Notice that Android uses the Cursor class as a return value for queries. Think of the
Cursor as a pointer to the result set from a database query. Using Cursor enables
Android to more efficiently manage rows and columns as needed.
You use a ContentValues object to store key/value pairs. Its put() method enables you
to insert keys with values of different data types.

Using the Database Programmatically


You are now ready to use the database using the helper class created in the previous section.

Adding Contacts
The following Try It Out demonstrates how you can add a contact to the table.

Adding Contacts to a Table


1. Using the same project created earlier, add the following statements in bold to the MainActivity
.java file:
package net.learn2develop.Databases;

import android.app.Activity;import android.os.Bundle;

public class MainActivity extends Activity {


/** Called when the activity is first created. */@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

DBAdapter db = new DBAdapter(this);

//---add a contact---db.open();
long id = db.insertContact(“Wei-Meng Lee”, “[email protected]”); id =
db.insertContact(“Mary Jackson”, “[email protected]”);
db.close();
}
}
2. Press F11 to debug the application on the Android Emulator.
How It Works
In this example, you first created an instance of the DBAdapter class:
DBAdapter db = new DBAdapter(this);

The insertContact() method returns the ID of the inserted row. If an error occurs during the
operation, it returns -1.
If you examine the file system of the Android device/emulator using DDMS, you can see that the
MyDB database is created under the databases folder (see
Figure 6-11). FIGURE 6-11

Retrieving All the Contacts


To retrieve all the contacts in the contacts table, use the getAllContacts() method of the DBAdapter
class, as the following Try It Out shows.

Retrieving All Contacts from a Table


1. Using the same project created earlier, add the following statements in bold to the MainActivity

.java file:
package net.learn2develop.Databases;

import android.app.Activity;import android.os.Bundle; import android.widget.Toast;

import android.database.Cursor;

public class MainActivity extends Activity {


/** Called when the activity is first created. */@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

DBAdapter db = new DBAdapter(this);

/*
//---add a contact---db.open();
long id = db.insertContact(“Wei-Meng Lee”, “[email protected]”); id =
db.insertContact(“Mary Jackson”, “[email protected]”);
db.close();
*/

//---get all contacts---db.open();


Cursor c = db.getAllContacts();if (c.moveToFirst())
{
do { DisplayContact(c);
} while (c.moveToNext());
}
db.close();
}

public void DisplayContact(Cursor c)


{
Toast.makeText(this,
“id: “ + c.getString(0) + “\n” +

“Name: “ + c.getString(1) + “\n” +“Email: “ + c.getString(2), Toast.LENGTH_LONG).show();


}
}
2. Press F11 to debug the application on the Android Emulator. Figure 6-12 shows the Toast class
displaying the contacts retrieved from the database.

FIGURE 6-12

How It Works
The getAllContacts() method of the DBAdapter class retrieves all the contacts stored in the database. The
result is returned as a Cursor object. To display all the contacts, you first need to call the
moveToFirst()method of the Cursor object. If it succeeds (which means at least one row is available),
display the details of the contact using the DisplayContact() method. To move to the next contact, call
the moveToNext() method of the Cursor object.

Retrieving a Single Contact


To retrieve a single contact using its ID, call the getContact() method of the DBAdapter class, as the
following Try It Out shows.

Retrieving a Contact from a Table


1. Using the same project created earlier, add the following statements in bold to the MainActivity
.java file:
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

DBAdapter db = new DBAdapter(this);

/*
//---add a contact---
//...
*/

/*

//---get all contacts---


//...
*/

//---get a contact---db.open();
Cursor c = db.getContact(2);if (c.moveToFirst())
DisplayContact(c);else
Toast.makeText(this, “No contact found”, Toast.LENGTH_LONG).show();db.close();
}
2. Press F11 to debug the application on the Android Emulator. The details of the second contact will
be displayed using the Toast class.
How It Works
The getContact() method of the DBAdapter class retrieves a single contact using its ID. You passed
in the ID of the contact; in this case, you passed in an ID of 2 to indicate that you want to retrieve
the second contact:
Cursor c = db.getContact(2);

The result is returned as a Cursor object. If a row is returned, you display the details of the contact
using the DisplayContact() method; otherwise, you display a message using the Toast class.

Updating a Contact
To update a particular contact, call the updateContact() method in the DBAdapter class by passing the
ID of the contact you want to update, as the following Try It Out shows.

Updating a Contact in a Table


1. Using the same project created earlier, add the following statements in bold to the MainActivity
.java file:
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

DBAdapter db = new DBAdapter(this);

/*
//---add a contact---
//...
*/

/*
//---get all contacts---
//...
*/

/*
//...

*/

//---update contact---db.open();
if (db.updateContact(1, “Wei-Meng Lee”, “[email protected]”)) Toast.makeText(this, “Update
successful.”, Toast.LENGTH_LONG).show();
else
Toast.makeText(this, “Update failed.”, Toast.LENGTH_LONG).show();db.close();
}
2. Press F11 to debug the application on the Android Emulator. A message will be displayed if the
update is successful.
How It Works
The updateContact() method in the DBAdapter class updates a contact’s details by using the ID of the
contact you want to update. It returns a Boolean value, indicating whether the update was
successful.

Deleting a Contact
To delete a contact, use the deleteContact() method in the DBAdapter class by passing the ID of the
contact you want to update, as the following Try It Out shows.

Deleting a Contact from a Table


1. Using the same project created earlier, add the following statements in bold to the MainActivity
.java file:
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);

DBAdapter db = new DBAdapter(this);

/*
//---add a contact---
//...
*/

/*
//---get all contacts---
//...
*/

/*
//---get a contact---
//...
*/

/*
//---update contact---

//...
*/

//---delete a contact---db.open();
if (db.deleteContact(1))
Toast.makeText(this, “Delete successful.”, Toast.LENGTH_LONG).show();else
Toast.makeText(this, “Delete failed.”, Toast.LENGTH_LONG).show();db.close();
}
2. Press F11 to debug the application on the Android Emulator. A message will be displayed if the
deletion was successful.
How It Works
The deleteContact() method in the DBAdapter class deletes a contact using the ID of the contact you
want to update. It returns a Boolean value, indicating whether the deletion was successful.

Content Providers in Android

A content provider is a library in Android that manages access to a central repository of


data. Content providers are part of the Android Framework, which provides user interface
(UI) features.

Content providers can:

 Help applications manage access to data


 Share data with other apps
 Offer granular control over the permissions for accessing data
 Allow other applications to securely access or modify data

A content provider behaves very much like a database — you can query it, edit its
content, as well as add or delete its content. However, unlike a database, a content
provider can use different ways to store its data. The data can be stored in a database, in
files, or even over a network.

Android ships with many useful content providers, including the following:

 Browser — Stores data such as browser bookmarks, browser history, and so on


 CallLog — Stores data such as missed calls, call details, and so on
 Contacts — Stores contact details
 MediaStore — Stores media files such as audio, video and images
 Settings — Stores the device’s settings and preferences

Content URI

Content URI(Uniform Resource Identifier) is the key concept of Content providers. To


access the data from a content provider, URI is used as a query string.

Structure of a Content URI: content://authority/optionalPath/optionalID

Details of different parts of Content URI:

 content:// – Mandatory part of the URI as it represents that the given URI is a Content
URI.
 authority – Signifies the name of the content provider like contacts, browser, etc. This part
must be unique for every content provider.
 optionalPath – Specifies the type of data provided by the content provider. It is essential
as this part helps content providers to support different types of data that are not related to
each other like audio and video files.
 optionalID – It is a numeric value that is used when there is a need to access a particular
record.

If an ID is mentioned in a URI then it is an id-based URI otherwise a directory-


based URI.

Operations in Content Provider

Four fundamental operations are possible in Content Provider namely Create, Read,
Update, and Delete. These operations are often termed as CRUD operations.

 Create: Operation to create data in a content provider.


 Read: Used to fetch data from a content provider.
 Update: To modify existing data.
 Delete: To remove existing data from the storage.

Working of the Content Provider

UI components of android applications like Activity and Fragments use an object


CursorLoader to send query requests to ContentResolver. The ContentResolver object
sends requests (like create, read, update, and delete) to the ContentProvider as a client.

After receiving a request, ContentProvider process it and returns the desired result. Below
is a diagram to represent these processes in pictorial form.

Create Content Provider

This involves number of simple steps to create your own content provider.

 First of all you need to create a Content Provider class that extends the
ContentProviderbaseclass.
 Second, you need to define your content provider URI address which will be used to access
the content.
 Next you will need to create your own database to keep the content. Usually, Android uses
SQLite database and framework needs to override onCreate() method which will use
SQLite Open Helper method to create or open the provider's database. When your
application is launched, the onCreate() handler of each of its Content Providers is called
on the main application thread.
 Next you will have to implement Content Provider queries to perform different database
specific operations.
 Finally register your Content Provider in your activity file using <provider> tag.

Following are the six abstract methods and their description which are essential to
override as the part of ContenProvider class:

Abstract MethodDescription
A method that accepts arguments and fetches the data from the
query()
desired table. Data is retired as a cursor object.
To insert a new row in the database of the content provider.
insert()
It returns the content URI of the inserted row.
This method is used to update the fields of an existing row.
update()
It returns the number of rows updated.
This method is used to delete the existing rows.
delete()
It returns the number of rows deleted.
This method returns the Multipurpose Internet Mail Extension(MIME)
getType()
type of data to the given Content URI.
As the content provider is created, the android system calls
onCreate()
this method immediately to initialise the provider.

Accessing a provider

An application accesses the data from a content provider with a ContentResolver client
object. This object has methods that call identically-named methods in the provider
object, an instance of one of the concrete subclasses of ContentProvider. The
ContentResolver methods provide the basic "CRUD" (create, retrieve, update, and delete)
functions of persistent storage.

The ContentResolver object in the client application's process and the ContentProvider
object in the application that owns the provider automatically handle inter-process
communication. ContentProvider also acts as an abstraction layer between its repository
of data and the external appearance of data as tables.

Note: To access a provider, your application usually has to request specific permissions
in its manifest file. This is described in more detail in the section Content Provider
Permissions

For example, to get a list of the words and their locales from the User Dictionary
Provider, you call ContentResolver.query(). The query() method calls the
ContentProvider.query() method defined by the User Dictionary Provider. The following
lines of code show a ContentResolver.query() call:

// Queries the user dictionary and returns results


mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
mProjection, // The columns to return for each row
mSelectionClause // Selection criteria

mSelectionArgs, // Selection criteria


mSortOrder); // The sort order for the returned rows

Table 2 shows how the arguments to


query(Uri,projection,selection,selectionArgs,sortOrder) match an SQL SELECT
statement:

Table 2: Query() compared to SQL query.

query() SELECT
Notes
argument keyword/parameter
Uri maps to the table in the
Uri FROM table_name
provider named table_name.
projection is an array of
columns that should be
projection col,col,col,...
included for each row
retrieved.
selection specifies the criteria
selection WHERE col = value
for selecting rows.
(No exact
equivalent. Selection
selectionArgs arguments replace ?
placeholders in the
selection clause.)
sortOrder specifies the order in
ORDER BY
sortOrder which rows appear in the
col,col,...
returned Cursor.

You might also like