top of page

Exploring Android Application Components in detail: Broadcast Receivers, Content Providers



Broadcast Receivers


Broadcast Receivers are one of the four main components of an Android application, along with Activities, Services, and Content Providers. They are responsible for receiving and reacting to system-wide broadcast messages, which are messages sent by the system or other applications to notify your application about a particular event.


How Broadcast Receivers work?


Broadcast Receivers are registered in the application's manifest file and are notified by the system when a matching broadcast message is sent. The system broadcasts messages using a specific action string that identifies the type of message being sent. Your application can register to receive specific types of messages by declaring a <receiver> element in the manifest file with an <intent-filter> element that specifies the action strings it is interested in.

When a matching broadcast message is sent, the system delivers the message to all registered receivers that have a matching intent filter. The receiver can then perform any necessary actions, such as updating the UI or performing background processing.


Types of Broadcast Receivers


There are two types of Broadcast Receivers in Android: Static Receivers and Dynamic Receivers.

  1. Static Receivers: These receivers are defined in the manifest file and are instantiated by the system when a matching broadcast is received. They are always active and can receive broadcast messages even when the application is not running.

  2. Dynamic Receivers: These receivers are created and registered at runtime by the application code. They are active only while the application is running and can be used to receive broadcast messages for specific situations.


Example of Broadcast Receiver


Here's an example of a simple Broadcast Receiver that receives a system broadcast message when the device's battery level changes:

First, define the <receiver> element in the application's manifest file:


<receiver android:name=".BatteryReceiver">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>

This defines a BatteryReceiver class as the receiver for the BATTERY_CHANGED broadcast message.

Implement the BatteryReceiver class:


public class BatteryReceiver extends BroadcastReceiver {

    @Override
public void onReceive(Context context, Intent intent) {
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        float batteryLevel = level / (float) scale;

        Log.d("BatteryLevel", "Battery level is " + batteryLevel);
    }
}

This receiver logs the battery level of the device when the BATTERY_CHANGED broadcast message is received.

Finally, register the receiver in the onCreate() method of an Activity or Service:


BatteryReceiver receiver = new BatteryReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(receiver, filter);

This registers the BatteryReceiver to receive the BATTERY_CHANGED broadcast message.


Broadcast Receivers are a powerful tool for building responsive and efficient Android applications. By registering receivers for specific types of broadcast messages, you can ensure that your application is always up-to-date and ready to respond to important system events.

It will give output as follows -


Content Provider


Content Providers in Android are one of the four main building blocks of an Android app, alongside Activities, Services, and Broadcast Receivers. Content providers are used to manage access to a structured set of data, typically stored in a SQLite database, or a remote server, and provide mechanisms for other apps to query, insert, update, and delete data.


Content providers are primarily used to manage data shared across multiple applications. They provide a standard interface to access the data, ensuring that the data is consistent and that it can be shared among different applications. The Android platform includes several built-in content providers for commonly used data types such as contacts, media files, and settings.

Here are the steps involved in creating a Content Provider in Android:

  1. Define a Contract: First, define a contract that specifies the structure of the data to be managed by the Content Provider. The contract should define the data tables, columns, URIs, and other metadata associated with the data.

  2. Implement a SQLite Database: Implement a SQLite database to store the data. Use the contract to define the database schema and the SQL statements needed to create, query, update, and delete data.

  3. Implement a Content Provider: Implement a Content Provider class that manages access to the data stored in the SQLite database. The Content Provider class should implement the standard CRUD (create, read, update, delete) operations for the data, and expose them through a set of URIs.

  4. Register the Content Provider: Register the Content Provider with the Android system by adding the provider tag to the app's AndroidManifest.xml file.

  5. Access the Content Provider: Finally, access the Content Provider from other applications by querying the provider for its URIs and calling the appropriate methods to read or modify the data.

Here is an example of creating a Content Provider that manages a list of books:


Define a Contract: Define a contract class that specifies the structure of the data to be managed by the Content Provider. The contract should define the data tables, columns, URIs, and other metadata associated with the data. For example:


public class BookContract {
    public static final String CONTENT_AUTHORITY = "com.example.books";
    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

    public static class BookEntry implements BaseColumns {
        public static final String TABLE_NAME = "books";
        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
        public static final String COLUMN_TITLE = "title";
        public static final String COLUMN_AUTHOR = "author";
        public static final String COLUMN_YEAR = "year";
    }
}

Implement a SQLite Database: Implement a SQLite database to store the data. Use the contract to define the database schema and the SQL statements needed to create, query, update, and delete data.

For example:


public class BookDbHelper extends SQLiteOpenHelper {
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "books.db";

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

    @Overridepublic void onCreate(SQLiteDatabase db) {
        final String SQL_CREATE_BOOKS_TABLE = 
    "CREATE TABLE " + BookContract.BookEntry.TABLE_NAME + " (" +
      BookContract.BookEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
       BookContract.BookEntry.COLUMN_TITLE + " TEXT NOT NULL, " +
       BookContract.BookEntry.COLUMN_AUTHOR + " TEXT NOT NULL, " +
       BookContract.BookEntry.COLUMN_YEAR + " INTEGER NOT NULL " +" );";

        db.execSQL(SQL_CREATE_BOOKS_TABLE);
    }

    @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is to simply discard the data and start over

db.execSQL("DROP TABLE IF EXISTS " + BookContract.BookEntry.TABLE_NAME);
        onCreate(db);
    }
}

This is a sample implementation of a SQLiteOpenHelper class that is used to manage the creation and upgrade of a database for a book app. The onCreate method is called when the database is created for the first time and it creates a new table named books with columns for book title, author, and year. The onUpgrade method is called when the database needs to be upgraded to a new version and it drops the existing table and creates a new one.


Types of Content Provider -


There are many different types of content providers, depending on the type of data they provide. Some of the most common types include:

  1. File-based content providers: These content providers provide access to files on the device, such as photos or videos.

  2. Database-based content providers: These content providers provide access to data stored in a SQLite database.

  3. Contact-based content providers: These content providers provide access to contact information stored on the device.

  4. Media-based content providers: These content providers provide access to media files such as audio or video files.

To create a content provider in Android, you need to create a class that extends the ContentProvider base class and implement the required methods. The most important method to implement is the query() method, which is responsible for returning a cursor object containing the requested data.

Here's an example of a simple User provider that provides access to a SQLite database:


public class UsersProvider extends ContentProvider {

  static final String PROVIDER_NAME =  "com.contentprovider.UserProvider";
    static final String URL = "content://" + PROVIDER_NAME + "/users";
    static final Uri CONTENT_URI = Uri.parse(URL);

    static final String id = "id";
    static final String name = "name";
    static final int uriCode = 1;
    static final UriMatcher uriMatcher;
    private static HashMap<String, String> values;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(PROVIDER_NAME, "users", uriCode);
        uriMatcher.addURI(PROVIDER_NAME, "users/*", uriCode);
    }

    private SQLiteDatabase db;
    static final String DATABASE_NAME = "EmpDB";
    static final String TABLE_NAME = "Employees";
    static final int DATABASE_VERSION = 1;
    static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME
            + " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
            + " name TEXT NOT NULL);";

    @Override
public boolean onCreate() {
        Context context = getContext();
        DatabaseHelper dbHelper = new DatabaseHelper(context);
        db = dbHelper.getWritableDatabase();
        if (db != null) {
            return true;
        }
        return false;
    }

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

        switch (uriMatcher.match(uri)) {
            case uriCode:
                qb.setProjectionMap(values);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        if (sortOrder == null || sortOrder == "") {
            sortOrder = id;
        }

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

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

    @Override
public Uri insert(Uri uri, ContentValues values) {
        long rowID = db.insert(TABLE_NAME, "", values);
        if (rowID > 0) {
            Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
            getContext().getContentResolver().notifyChange(_uri, null);
            return _uri;
        }
        throw new SQLiteException("Failed to add a record into " + uri);
    }

    @Override
public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)) {
            case uriCode:
         count = db.update(TABLE_NAME, values, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)) {
            case uriCode:
                count = db.delete(TABLE_NAME, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    @Override
public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case uriCode:
                return "vnd.android.cursor.dir/users";
            default:
           throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

In this example, we define a content provider that provides access to a table in a SQLite database. We define the authority and base path of the content provider, and we create a content URI that identifies the data. We also implement the onCreate() method, which initializes the database, and the query() method, which queries the database and returns a cursor object. The MainActivity is set as the launcher activity using an intent filter, which means it will be the first activity shown when the app is launched. It will look like this -


public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    return true;
}

public void onClickAddDetails(View view) {
    ContentValues values = new ContentValues();
    values.put(UsersProvider.name, ((EditText) findViewById(R.id.txtName)).getText().toString());
    getContentResolver().insert(UsersProvider.CONTENT_URI, values);
    Toast.makeText(getBaseContext(), "New Record Inserted", Toast.LENGTH_LONG).show();
}

@SuppressLint("Range")
public void onClickShowDetails(View view) {
    // Retrieve employee records
    TextView resultView= (TextView) findViewById(R.id.res);
    Cursor cursor = getContentResolver().query(Uri.parse("content://com.contentprovider.UserProvider/users"), null, null, null, null);
    if(cursor.moveToFirst()) {
        StringBuilder strBuild=new StringBuilder();
        while (!cursor.isAfterLast()) {
            strBuild.append("\n"+cursor.getString(cursor.getColumnIndex("id"))+ "-"+ cursor.getString(cursor.getColumnIndex("name")));
            cursor.moveToNext();
        }
        resultView.setText(strBuild);
    }
    else {
        resultView.setText("No Records Found");
    }
}
}

To use this content provider in your app, you need to declare it in your AndroidManifest.xml file and specify the required permissions. You can then use the content provider's content URI to access the data from other components in your app or from other apps.

The app will look like this before and after adding users -



Thanks for reading, and happy coding!


Master Android Development in Java Series part-7 -> Exploring Android Application Components in detail: Fragments, Intents


bottom of page