Skip to main content

Command Palette

Search for a command to run...

SQLite in Android: Architecture, Benefits, Limitations, and a Practical Student Database Example

Updated
15 min read
S

I am an IT student with a strong passion and proficiency in programming. I have solid experience working with.

Introduction

In the realm of mobile application development, managing data efficiently is crucial. While cloud-based solutions are popular, there is a strong need for a reliable, offline-first database that runs directly on the device. Enter SQLite.

This article explores SQLite, the embedded relational database management system (RDBMS) at the heart of Android's data persistence strategy. We will break down its architecture, advantages, limitations, and how Android bridges the gap between C-based SQLite and Java/Kotlin applications.

What Is SQLite?

SQLite is an embedded, relational database management system (RDBMS). Unlike traditional databases such as MySQL or PostgreSQL that run as separate server processes, SQLite is provided in the form of a library linked directly into your application.

Key Characteristics

Characteristic Description
Serverless No standalone database server. All operations are handled internally via functions from the SQLite library.
File-Based Reads and writes directly to ordinary disk files.
Single File A complete database with multiple tables, indices, triggers, and views is contained in one disk file.
Cross-Platform Database files can be freely copied between 32-bit and 64-bit systems.
Zero Configuration No installation or setup required.

Figure 1: A database can contain multiple tables, each designed for a specific type of information.

Understanding Database Tables: Rows and Columns

It is helpful to view a database table as being similar to a spreadsheet such as Microsoft Excel:

  • Columns represent data fields in the corresponding table.

  • Rows, also called records or entries, represent information. Each new record saved to a table is stored in a row.

  • Each table has a name that must be unique within that particular database.

Database Schema

The database schema defines the characteristics of the data stored in a database table. It is also used to define:

  • The structure of entire databases.

  • The relationships between various tables contained in a database.

Primary Key

A database table must contain one column that can be used to uniquely identify each row in the table. This is called the primary key.

Remember: An index is used to query data faster, speed up sort operations, and enforce unique constraints.

Figure 2: Columns represent data fields. Rows represent records or entries.

What Is a Relational Database System?

A Relational Database System (RDS) contains one or more objects called tables. The data or information for the database are stored in these tables.

Many companies use a database management system that has SQL already built into it. Common RDBMS that use SQL include:

RDBMS Vendor
Oracle Oracle Corporation
Sybase SAP
Microsoft SQL Server Microsoft
Microsoft Access Microsoft
MySQL Oracle (originally MySQL AB)
SQLite Public Domain

Figure 3: A Relational Database System contains one or more objects called tables.

Figure 4: Common Relational Database Management Systems that use SQL.

Key takeaway: For all of these there is SQL (Structured Query Language).

What Is SQL?

SQL stands for Structured Query Language.

SQL is used to communicate with a database. It is a programming language that is used to communicate with and manipulate databases.

History of SQL

The SQL programming language was first developed in the 1970s by IBM researchers Raymond Boyce and Donald Chamberlin. The programming language, known then as SEQUEL, was created following the publishing of Edgar Frank Codd's paper, "A Relational Model of Data for Large Shared Data Banks," in 1970.

SQL Standard

According to ANSI (American National Standards Institute), SQL is the standard language for relational database management systems.

What Can SQL Do?

SQL statements are used to perform tasks such as:

  • Update data in a database.

  • Retrieve data from a database.

  • Query the database.

MySQL

Developed and distributed by Oracle, MySQL is one of the most popular SQL database management systems currently available. The software is an open-source version, which means it can be downloaded and used for free.

According to the web hosting service GoDaddy, MySQL is a sophisticated and powerful relational database management system used by many websites to create and change content quickly.

SQLite in Android

SQLite is an embedded, relational database management system with these key characteristics:

Feature Description
Embedded Provided in the form of a library, linked into applications.
No Server No standalone database server process.
Internal Operations All database operations are handled internally using functions from the SQLite library.
File-Based I/O Reads and writes directly to ordinary disk files.
Single File Database Complete SQL database with multiple tables contained in a single disk file.
Cross-Platform Database file format is cross-platform and can be freely copied between 32-bit and 64-bit systems.

The Language Gap

SQLite is written in C language, but Android apps are written in Java, and now Kotlin.

So how do they work together?

To bridge this language gap, the Android SDK includes a set of classes that provide a Java layer on top of the SQLite database management system.

Indexes in SQLite

SQLite supports different types of indexes:

Index Type Purpose
Unique Indexes Used for performance and data integrity. Does not allow duplicate values.
Composite Index An index on two or more columns of a table.

Best practice: Indexes should not be used in small tables or tables with columns that contain a high number of NULL values.

Android SQLite Java Classes

The Cursor Class

The Cursor class exposes results from a query on a SQLiteDatabase.

Class hierarchy:

java.lang.Object
-> android.database.AbstractCursor
-> android.database.AbstractWindowedCursor
-> android.database.sqlite.SQLiteCursor

Important Cursor methods:

Method Description
close() Releases all resources used by the cursor and closes it.
getCount() Returns the number of rows/records within the result set.
moveToFirst() Moves to the first row/record within the result set.
moveToLast() Moves to the last row/record in the result set.
moveToNext() Moves to the next row/record in the result set.
move(offset) Moves by a specified offset from the current position.
getString() Returns String value at specified column index.
getInt() Returns Integer value at specified column index.
getShort() Returns Short value at specified column index.
getFloat() Returns Float value at specified column index.
getDouble() Returns Double value at specified column index.

SQLiteDatabase Class

Method Description
insert() Inserts a new row into a database table.
delete() Deletes rows from a database table.
query() Performs a database query and returns results via a Cursor object.
execSQL() Executes a single SQL statement that does not return result data.
rawQuery() Executes an SQL query and returns results in a Cursor object.

SQLiteOpenHelper Class

Method Description
onCreate() Called when the database is created for the first time.
onUpgrade() Called when app code contains a more recent database version number.
getWritableDatabase() Opens or creates a database for reading and writing. Returns a SQLiteDatabase object.
getReadableDatabase() Creates or opens a database for reading only. Returns a SQLiteDatabase object.
close() Closes the database.

More methods are available at developer.android.com/reference/android/database/sqlite/.

SQLite Advantages for Mobile Development

# Advantage Description
1 Free and Public Domain Free for any purpose, commercial or private.
2 No Separate Server Reads and writes directly to ordinary disk files.
3 Single File Complete database with multiple tables, indices, triggers, and views in one file.
4 Full Offline Mode Apps that depend on stored data work without internet.
5 Stable Performance Predictable performance independent from network availability.
6 Data Privacy (Safe) Personal data stored only on your device.
7 Custom Schema Developers can define exactly the data schema they want.
8 Full Control Handwritten SQL queries give complete control.
9 Powerful Query Language SQL supports most standard features.
10 Debuggable Developers can grab the database file and analyze it.
11 Rock-Solid Widely used technology established since year 2000.
12 No Setup Required No need to set up a database server and interface.
13 Secure by Default Android stores the database in the app's private folder, inaccessible to other apps or users.
14 Native Support Android has built-in native support for SQLite.

SQLite Disadvantages for Mobile Development

# Disadvantage Description
1 Lot of Boilerplate Code Much code is required, leading to inefficiencies in long-term maintenance.
2 No Compile-Time Checks SQL queries are strings, so errors can cause runtime crashes.
3 Unreliable Performance Different devices produce different performance results.
4 Another Language to Master SQL is an additional language developers must learn.
5 Complex Queries SQL queries can get long and complicated.
6 Slower Performance SQLite must access the file system, which is slower than memory.
7 Limited Database Size Only for local and small databases, with read/query under 5 ms.
8 Storage Limitations Mobile device storage constraints prevent huge databases.

Recommendation: If you need a database for a huge amount of data, better use a remote DB on an external server.

ORM and Realm

An ORM (Object-Relational Mapper) keeps SQLite abstraction by letting database entities map to Java objects more easily.

Realm (Introduced June 2014)

Realm Mobile Database is a database designed specifically for mobile devices from the ground up.

Feature Description
Not an ORM Not an abstraction built on top of SQLite.
New Engine A whole new database engine was created.
Object Store Based on an object store rather than a relational model.
C++ Core Core consists of a self-contained C++ library.
Platform Support Android, iOS (Objective-C and Swift), Xamarin, and React Native.

Learn more at realm.io.

Android Layout Fundamentals

When building the UI for your database app, understanding layout parameters is essential.

Concept Meaning
match_parent Makes the view expand to as much space as possible horizontally and vertically within the parent view. The view will be as big as its parent. In general, parent_view is not the same size as a mobile display.
wrap_content Takes only enough space for its contents to get enclosed. It does not waste space.
android:orientation Specifies the layout direction. Values: vertical stacks items top-to-bottom, while horizontal arranges items side-by-side.
Color Hex Code RGB Breakdown
White #FFFFFF 33% Red + 33% Green + 33% Blue
Black #000000 0% Red + 0% Green + 0% Blue

android:background="#ffffff" sets a white background.

Figure 5: android:orientation="vertical" specifies the layout direction by stacking items top-to-bottom.

Figure 6: match_parent expands fully, while wrap_content shrinks to fit content only.

Practical Application: Student Database Example

Goal

An Android app for managing a sample student database (EnrollNumber, StudentName, and StudentMarks). The user's operations will be: Add, Modify, Delete, Search, and ViewAll records.

Database Schema

Database Element Value
Database Name StudentDB (or BookDB in examples)
Table Name Students (or Books in examples)
Columns ID, EnrollNumber, StudentName, StudentMarks

Column data types:

Column Data Type Description
id INTEGER PRIMARY KEY AUTOINCREMENT Unique identifier for each record.
enroll_number TEXT or INTEGER Student's enrollment number.
student_name TEXT Student's full name.
student_marks REAL or INTEGER Student's marks or grades.

Figure 7: The name and structure or columns of the database table.

Required Imports for Database Operations

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

Creating the Database

The function used to create the database is:

db.execSQL(CREATE_BOOK_TABLE);

Where CREATE_BOOK_TABLE is a String:

String CREATE_BOOK_TABLE = "CREATE TABLE books (" +
    "id INTEGER PRIMARY KEY AUTOINCREMENT," +
    "title TEXT," +
    "author TEXT)";

Note: This is the first operation needed if no database exists.

Sample UI Layout (XML)

<TextView
    android:text="Student Data"
    android:layout_x="110dp"
    android:layout_y="10dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:text="Enroll No.: "
    android:layout_x="30dp"
    android:layout_y="48dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<EditText
    android:id="@+id/editEnrollno"
    android:layout_x="155dp"
    android:layout_y="33dp"
    android:layout_width="150dp"
    android:layout_height="50dp" />

<TextView
    android:text="Name: "
    android:layout_x="30dp"
    android:layout_y="100dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<EditText
    android:id="@+id/editName"
    android:inputType="text"
    android:layout_x="155dp"
    android:layout_y="81dp"
    android:layout_width="150dp"
    android:layout_height="50dp" />

<TextView
    android:text="Marks: "
    android:layout_x="30dp"
    android:layout_y="150dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<EditText
    android:id="@+id/editMarks"
    android:inputType="numberDecimal"
    android:layout_x="155dp"
    android:layout_y="128dp"
    android:layout_width="150dp"
    android:layout_height="50dp" />

<Button
    android:id="@+id/btnAdd"
    android:text="Add"
    android:layout_x="30dp"
    android:layout_y="200dp"
    android:layout_width="100dp"
    android:layout_height="40dp" />

<Button
    android:id="@+id/btnDelete"
    android:text="Delete"
    android:layout_x="250dp"
    android:layout_y="200dp"
    android:layout_width="100dp"
    android:layout_height="40dp" />

<Button
    android:id="@+id/btnModify"
    android:text="Modify"
    android:layout_x="140dp"
    android:layout_y="200dp"
    android:layout_width="100dp"
    android:layout_height="40dp" />

<Button
    android:id="@+id/btnSearch"
    android:text="Search"
    android:layout_x="85dp"
    android:layout_y="250dp"
    android:layout_width="100dp"
    android:layout_height="40dp" />

<Button
    android:id="@+id/btnViewAll"
    android:text="View All"
    android:layout_x="195dp"
    android:layout_y="250dp"
    android:layout_width="100dp"
    android:layout_height="40dp" />

Strings.xml Resource Values

Resource Value
App name Student Database App
Heading Student Data
Label Enroll No.:
Label Name:
Label Marks:
Button Add
Button Delete
Button Modify
Button Search
Button View All

Complete Code Example Structure

public class MainActivity extends AppCompatActivity {

    // Database Helper Class
    public class DatabaseHelper extends SQLiteOpenHelper {

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

        @Override
        public void onCreate(SQLiteDatabase db) {
            String CREATE_TABLE = "CREATE TABLE students (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "enroll_no TEXT," +
                "name TEXT," +
                "marks TEXT)";
            db.execSQL(CREATE_TABLE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS students");
            onCreate(db);
        }

        // CRUD Operations
        public boolean addStudent(String enrollNo, String name, String marks) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("enroll_no", enrollNo);
            values.put("name", name);
            values.put("marks", marks);
            long result = db.insert("students", null, values);
            return result != -1;
        }

        public Cursor getAllStudents() {
            SQLiteDatabase db = this.getReadableDatabase();
            return db.rawQuery("SELECT * FROM students", null);
        }

        public Cursor searchStudent(String enrollNo) {
            SQLiteDatabase db = this.getReadableDatabase();
            return db.rawQuery("SELECT * FROM students WHERE enroll_no = ?",
                new String[]{enrollNo});
        }

        public boolean updateStudent(String enrollNo, String name, String marks) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("name", name);
            values.put("marks", marks);
            int result = db.update("students", values, "enroll_no = ?",
                new String[]{enrollNo});
            return result > 0;
        }

        public boolean deleteStudent(String enrollNo) {
            SQLiteDatabase db = this.getWritableDatabase();
            int result = db.delete("students", "enroll_no = ?",
                new String[]{enrollNo});
            return result > 0;
        }
    }
}

Conclusion

SQLite remains a critical skill for Android developers. Despite its verbosity compared to modern ORMs, understanding SQLite means understanding the fundamentals of data persistence on constrained devices. It offers stability, full control, and security that external databases cannot guarantee when the device is offline.

Key Takeaways

Concept Key Point
SQLite Embedded, serverless, file-based RDBMS
Language Bridge Android SDK provides a Java layer over C-based SQLite
Main Classes SQLiteOpenHelper, SQLiteDatabase, Cursor
Best For Offline-first, local, small-to-medium data apps
Watch Out For Boilerplate code and no compile-time SQL checks

Future Work

To improve upon the standard SQLite implementation, developers should consider:

  • Implementing RecyclerView to display Cursor data efficiently.

  • Using Room Persistence Library, Google's official abstraction over SQLite, to reduce boilerplate and add compile-time SQL verification.

  • Encrypting the database using SQLCipher for sensitive data storage.

  • Migrating to modern alternatives like Realm for object-based storage.

References

  • Android Developers Documentation: android.database.sqlite

  • Realm Mobile Database: https://realm.io/

  • SQLite Official Documentation: https://www.sqlite.org/

30 views