Md Mominul Islam | Software and Data Enginnering | SQL Server, .NET, Power BI, Azure Blog

while(!(succeed=try()));

LinkedIn Portfolio Banner

Latest

Home Top Ad

Responsive Ads Here

Post Top Ad

Responsive Ads Here

Tuesday, August 26, 2025

Master Android App Development: Module 5 - Working with Data in Android Studio

 

Table of Contents

  1. Introduction to Data Management in Android

    • 1.1 Why Data Management Matters

    • 1.2 Overview of Data Storage Options

  2. SharedPreferences for Simple Data Storage

    • 2.1 What is SharedPreferences?

    • 2.2 Real-Life Use Cases

    • 2.3 Step-by-Step Implementation

    • 2.4 Best Practices and Exception Handling

    • 2.5 Pros, Cons, and Alternatives

  3. Introduction to Room Database for Persistent Storage

    • 3.1 What is Room Database?

    • 3.2 Real-Life Use Cases

    • 3.3 Setting Up Room in Android Studio

    • 3.4 Basic CRUD Operations with Room

    • 3.5 Best Practices and Exception Handling

    • 3.6 Pros, Cons, and Alternatives

  4. File Storage: Internal and External Storage

    • 4.1 Understanding File Storage in Android

    • 4.2 Internal Storage: Implementation and Examples

    • 4.3 External Storage: Implementation and Examples

    • 4.4 Best Practices and Exception Handling

    • 4.5 Pros, Cons, and Alternatives

  5. Handling JSON Data with Gson and Moshi

    • 5.1 Introduction to JSON Parsing

    • 5.2 Using Gson for JSON Parsing

    • 5.3 Using Moshi for JSON Parsing

    • 5.4 Real-Life Example: Fetching and Displaying Weather Data

    • 5.5 Best Practices and Exception Handling

    • 5.6 Pros, Cons, and Alternatives

  6. Practical Exercise: Building a Note-Taking App

    • 6.1 Project Overview

    • 6.2 Step-by-Step Implementation

    • 6.3 Testing and Debugging

    • 6.4 Enhancing the App with Advanced Features

  7. Conclusion and Next Steps

    • 7.1 Recap of Learning Outcomes

    • 7.2 Further Learning Resources


1. Introduction to Data Management in Android

1.1 Why Data Management Matters

Data is the backbone of any mobile application. Whether it's saving user preferences, storing a list of tasks, or caching API responses, effective data management ensures a seamless user experience. In Android, developers have multiple options to store and manage data, each suited for specific use cases. This module explores SharedPreferences, Room database, file storage, and JSON parsing, guiding you from beginner to advanced concepts with real-life examples.

1.2 Overview of Data Storage Options

Android provides several data storage mechanisms:

  • SharedPreferences: For lightweight key-value pair storage.

  • Room Database: For structured, persistent data storage.

  • File Storage: For saving files in internal or external storage.

  • JSON Parsing: For handling structured data from APIs or local files.

Each method has unique advantages, and choosing the right one depends on your app’s requirements.


2. SharedPreferences for Simple Data Storage

2.1 What is SharedPreferences?

SharedPreferences is a lightweight storage mechanism in Android for saving key-value pairs. It’s ideal for storing small amounts of data, such as user settings (e.g., theme preference) or login status.

2.2 Real-Life Use Cases

  • App Settings: Save user preferences like dark mode or notification settings.

  • User Sessions: Store login state or user ID.

  • Simple Counters: Track the number of app launches.

2.3 Step-by-Step Implementation

Let’s create a simple app that saves and retrieves a user’s preferred theme (light or dark) using SharedPreferences.

Step 1: Add Permissions (Optional)

No permissions are required for SharedPreferences as it uses internal storage.

Step 2: Create the UI

Create a layout with a toggle button to switch themes and a text view to display the current theme.

<!-- res/layout/activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/themeStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Current Theme: Light"/>

    <Button
        android:id="@+id/toggleThemeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Toggle Theme"/>
</LinearLayout>

Step 3: Implement SharedPreferences Logic

In your MainActivity, use SharedPreferences to save and retrieve the theme preference.

// app/src/main/java/com/example/myapp/MainActivity.java
package com.example.myapp;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private static final String PREFS_NAME = "MyPrefs";
    private static final String KEY_THEME = "theme";
    private TextView themeStatus;
    private boolean isDarkTheme;

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

        themeStatus = findViewById(R.id.themeStatus);
        Button toggleThemeButton = findViewById(R.id.toggleThemeButton);

        // Initialize SharedPreferences
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        isDarkTheme = prefs.getBoolean(KEY_THEME, false); // Default is light theme
        updateThemeUI();

        toggleThemeButton.setOnClickListener(v -> {
            isDarkTheme = !isDarkTheme;
            SharedPreferences.Editor editor = prefs.edit();
            editor.putBoolean(KEY_THEME, isDarkTheme);
            editor.apply(); // Asynchronous save
            updateThemeUI();
        });
    }

    private void updateThemeUI() {
        themeStatus.setText("Current Theme: " + (isDarkTheme ? "Dark" : "Light"));
        // Apply theme (simplified for example)
        getWindow().getDecorView().setBackgroundColor(isDarkTheme ? 0xFF333333 : 0xFFFFFFFF);
    }
}

Step 4: Test the App

Run the app, toggle the theme, and close/reopen it to verify that the theme preference persists.

2.4 Best Practices and Exception Handling

  • Use apply() for Asynchronous Saves: apply() is preferred over commit() for better performance.

  • Handle Default Values: Always provide default values when retrieving data to avoid null issues.

  • Clear Preferences When Needed:

SharedPreferences.Editor editor = prefs.edit();
editor.clear().apply(); // Clears all preferences
  • Exception Handling:

try {
    SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
    isDarkTheme = prefs.getBoolean(KEY_THEME, false);
} catch (ClassCastException e) {
    // Handle case where key exists but type is incorrect
    isDarkTheme = false;
}

2.5 Pros, Cons, and Alternatives

Pros:

  • Simple and lightweight.

  • No setup overhead.

  • Ideal for small data.

Cons:

  • Not suitable for complex or large datasets.

  • No querying capabilities.

Alternatives:

  • Room Database: For structured data.

  • File Storage: For larger, unstructured data.


3. Introduction to Room Database for Persistent Storage

3.1 What is Room Database?

Room is a persistence library provided by Google as part of Android Jetpack. It’s an abstraction layer over SQLite, making it easier to store and manage structured data.

3.2 Real-Life Use Cases

  • Note-Taking Apps: Store notes with titles, content, and timestamps.

  • E-Commerce Apps: Save product lists or cart items locally.

  • Fitness Trackers: Log workout data.

3.3 Setting Up Room in Android Studio

Step 1: Add Dependencies

Add Room dependencies to your build.gradle (app-level).

// app/build.gradle
dependencies {
    implementation "androidx.room:room-runtime:2.6.1"
    annotationProcessor "androidx.room:room-compiler:2.6.1"
    // For Kotlin (if used)
    kapt "androidx.room:room-compiler:2.6.1"
    implementation "androidx.room:room-ktx:2.6.1" // Optional for coroutines
}

Sync the project.

Step 2: Define the Entity

Create a Note entity to represent a note in the database.

// app/src/main/java/com/example/myapp/Note.java
package com.example.myapp;

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "notes")
public class Note {
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String title;
    private String content;
    private long timestamp;

    // Constructor
    public Note(String title, String content, long timestamp) {
        this.title = title;
        this.content = content;
        this.timestamp = timestamp;
    }

    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public long getTimestamp() { return timestamp; }
    public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
}

Step 3: Create the DAO

Define a Data Access Object (DAO) to handle database operations.

// app/src/main/java/com/example/myapp/NoteDao.java
package com.example.myapp;

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;

@Dao
public interface NoteDao {
    @Insert
    void insert(Note note);

    @Update
    void update(Note note);

    @Delete
    void delete(Note note);

    @Query("SELECT * FROM notes ORDER BY timestamp DESC")
    List<Note> getAllNotes();
}

Step 4: Set Up the Database

Create the Room database class.

// app/src/main/java/com/example/myapp/AppDatabase.java
package com.example.myapp;

import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {Note.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract NoteDao noteDao();
}

Step 5: Initialize the Database

Initialize Room in your activity or application class.

// In Application class or Activity
AppDatabase db = Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase.class,
    "note_database"
).build();

3.4 Basic CRUD Operations with Room

Create

Note note = new Note("Meeting", "Discuss project timeline", System.currentTimeMillis());
new Thread(() -> db.noteDao().insert(note)).start();

Read

List<Note> notes = db.noteDao().getAllNotes(); // Run on background thread

Update

note.setContent("Updated content");
new Thread(() -> db.noteDao().update(note)).start();

Delete

new Thread(() -> db.noteDao().delete(note)).start();

3.5 Best Practices and Exception Handling

  • Run Database Operations on Background Threads: Use AsyncTask, Executors, or coroutines to avoid blocking the UI thread.

  • Handle Database Migrations:

@Database(entities = {Note.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
    // Migration from version 1 to 2
    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE notes ADD COLUMN priority INTEGER");
        }
    };
}
  • Exception Handling:

try {
    db.noteDao().insert(note);
} catch (SQLiteConstraintException e) {
    // Handle constraint violations (e.g., duplicate primary key)
    Log.e("DatabaseError", "Insert failed: " + e.getMessage());
}

3.6 Pros, Cons, and Alternatives

Pros:

  • Structured data storage.

  • Built-in support for queries and relationships.

  • Type-safe with compile-time checks.

Cons:

  • Steeper learning curve than SharedPreferences.

  • Overhead for small datasets.

Alternatives:

  • SQLiteOpenHelper: Direct SQLite access (more manual).

  • Firebase Realtime Database: For cloud-based storage.


4. File Storage: Internal and External Storage

4.1 Understanding File Storage in Android

Android supports two types of file storage:

  • Internal Storage: Private to the app, not accessible by other apps.

  • External Storage: Shared storage (e.g., SD card or public directories).

4.2 Internal Storage: Implementation and Examples

Example: Save a Text File

// Save to internal storage
String filename = "myfile.txt";
String fileContents = "Hello, Android!";
try (FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE)) {
    fos.write(fileContents.getBytes());
} catch (IOException e) {
    Log.e("FileError", "Write failed: " + e.getMessage());
}

Read from Internal Storage

StringBuilder content = new StringBuilder();
try (FileInputStream fis = openFileInput(filename)) {
    int character;
    while ((character = fis.read()) != -1) {
        content.append((char) character);
    }
} catch (IOException e) {
    Log.e("FileError", "Read failed: " + e.getMessage());
}

4.3 External Storage: Implementation and Examples

Step 1: Add Permissions

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

Step 2: Check Permissions

if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}

Step 3: Save to External Storage

File file = new File(Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_DOCUMENTS), "myfile.txt");
try (FileOutputStream fos = new FileOutputStream(file)) {
    fos.write("Hello, Android!".getBytes());
} catch (IOException e) {
    Log.e("FileError", "Write failed: " + e.getMessage());
}

4.4 Best Practices and Exception Handling

  • Check Storage Availability:

String state = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(state)) {
    // Handle storage not available
}
  • Use Scoped Storage (Android 10+): Prefer Storage Access Framework (SAF) for better security.

  • Close Streams Properly: Use try-with-resources to avoid resource leaks.

4.5 Pros, Cons, and Alternatives

Pros:

  • Suitable for large files (e.g., images, videos).

  • Flexible for custom file formats.

Cons:

  • Requires permission handling for external storage.

  • No built-in querying like databases.

Alternatives:

  • Room Database: For structured data.

  • Cloud Storage: Firebase Storage for remote file storage.


5. Handling JSON Data with Gson and Moshi

5.1 Introduction to JSON Parsing

JSON (JavaScript Object Notation) is widely used for API responses and local data storage. Libraries like Gson and Moshi simplify parsing JSON into Java objects.

5.2 Using Gson for JSON Parsing

Step 1: Add Gson Dependency

implementation "com.google.code.gson:gson:2.10.1"

Step 2: Define a Data Model

// app/src/main/java/com/example/myapp/Weather.java
package com.example.myapp;

public class Weather {
    private String city;
    private double temperature;
    private String description;

    // Getters and Setters
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public double getTemperature() { return temperature; }
    public void setTemperature(double temperature) { this.temperature = temperature; }
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
}

Step 3: Parse JSON

String json = "{\"city\":\"New York\",\"temperature\":25.5,\"description\":\"Sunny\"}";
Gson gson = new Gson();
Weather weather = gson.fromJson(json, Weather.class);

5.3 Using Moshi for JSON Parsing

Step 1: Add Moshi Dependency

implementation "com.squareup.moshi:moshi:1.15.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.0"

Step 2: Parse JSON with Moshi

Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Weather> jsonAdapter = moshi.adapter(Weather.class);
Weather weather = jsonAdapter.fromJson(json);

5.4 Real-Life Example: Fetching and Displaying Weather Data

Assume you’re fetching weather data from an API (simulated here with a local JSON string).

// Display weather data
TextView weatherView = findViewById(R.id.weatherView);
weatherView.setText(String.format("City: %s\nTemp: %.1f°C\nDesc: %s",
        weather.getCity(), weather.getTemperature(), weather.getDescription()));

5.5 Best Practices and Exception Handling

  • Handle JSON Errors:

try {
    Weather weather = gson.fromJson(json, Weather.class);
} catch (JsonSyntaxException e) {
    Log.e("JsonError", "Parsing failed: " + e.getMessage());
}
  • Use Type Adapters for Complex JSON: For nested objects, define custom adapters in Gson or Moshi.

  • Validate JSON: Ensure the JSON structure matches the data model.

5.6 Pros, Cons, and Alternatives

Pros:

  • Gson: Simple and widely used.

  • Moshi: Lightweight and supports Kotlin natively.

Cons:

  • Gson: Slower for large datasets.

  • Moshi: Requires additional setup for Kotlin.

Alternatives:

  • Jackson: Another JSON parsing library.

  • Manual Parsing: Using JSONObject and JSONArray (not recommended).


6. Practical Exercise: Building a Note-Taking App

6.1 Project Overview

Let’s build a note-taking app that uses Room for persistent storage, SharedPreferences for settings, and JSON for exporting notes. The app allows users to create, read, update, and delete notes, with a setting to toggle the theme.

6.2 Step-by-Step Implementation

Step 1: Set Up the Project

Create a new Android Studio project with an Empty Activity. Add Room and Gson dependencies as shown earlier.

Step 2: Create the Database

Use the Note entity, NoteDao, and AppDatabase classes from Section 3.

Step 3: Create the UI

Main Layout (activity_main.xml):

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/notesRecyclerView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"/>

<Button
    android:id="@+id/addNoteButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Add Note"/>

<Button
    android:id="@+id/toggleThemeButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Toggle Theme"/>

Note Item Layout (item_note.xml):

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:id="@+id/noteTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>

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

Step 4: Implement the RecyclerView Adapter

// app/src/main/java/com/example/myapp/NoteAdapter.java
package com.example.myapp;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.NoteViewHolder> {
    private List<Note> notes;

    public NoteAdapter(List<Note> notes) {
        this.notes = notes;
    }

    @Override
    public NoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_note, parent, false);
        return new NoteViewHolder(view);
    }

    @Override
    public void onBindViewHolder(NoteViewHolder holder, int position) {
        Note note = notes.get(position);
        holder.title.setText(note.getTitle());
        holder.content.setText(note.getContent());
    }

    @Override
    public int getItemCount() {
        return notes.size();
    }

    public void updateNotes(List<Note> newNotes) {
        this.notes = newNotes;
        notifyDataSetChanged();
    }

    static class NoteViewHolder extends RecyclerView.ViewHolder {
        TextView title, content;

        NoteViewHolder(View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.noteTitle);
            content = itemView.findViewById(R.id.noteContent);
        }
    }
}

Step 5: Implement the Main Activity

// app/src/main/java/com/example/myapp/MainActivity.java
package com.example.myapp;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.room.Room;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private AppDatabase db;
    private NoteAdapter adapter;
    private boolean isDarkTheme;
    private static final String PREFS_NAME = "MyPrefs";
    private static final String KEY_THEME = "theme";

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

        // Initialize SharedPreferences
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        isDarkTheme = prefs.getBoolean(KEY_THEME, false);
        updateTheme();

        // Initialize Room
        db = Room.databaseBuilder(getApplicationContext(),
                AppDatabase.class, "note_database").build();

        // Set up RecyclerView
        RecyclerView recyclerView = findViewById(R.id.notesRecyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new NoteAdapter(new ArrayList<>());
        recyclerView.setAdapter(adapter);

        // Load notes
        loadNotes();

        // Add note button
        Button addNoteButton = findViewById(R.id.addNoteButton);
        addNoteButton.setOnClickListener(v -> {
            Note note = new Note("New Note", "Content", System.currentTimeMillis());
            new Thread(() -> {
                db.noteDao().insert(note);
                runOnUiThread(this::loadNotes);
            }).start();
        });

        // Toggle theme button
        Button toggleThemeButton = findViewById(R.id.toggleThemeButton);
        toggleThemeButton.setOnClickListener(v -> {
            isDarkTheme = !isDarkTheme;
            SharedPreferences.Editor editor = prefs.edit();
            editor.putBoolean(KEY_THEME, isDarkTheme);
            editor.apply();
            updateTheme();
        });
    }

    private void loadNotes() {
        new Thread(() -> {
            List<Note> notes = db.noteDao().getAllNotes();
            runOnUiThread(() -> adapter.updateNotes(notes));
        }).start();
    }

    private void updateTheme() {
        getWindow().getDecorView().setBackgroundColor(isDarkTheme ? 0xFF333333 : 0xFFFFFFFF);
    }
}

Step 6: Export Notes to JSON

Add a button to export notes as a JSON file using Gson.

Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(v -> {
    new Thread(() -> {
        List<Note> notes = db.noteDao().getAllNotes();
        Gson gson = new Gson();
        String json = gson.toJson(notes);
        try (FileOutputStream fos = openFileOutput("notes.json", Context.MODE_PRIVATE)) {
            fos.write(json.getBytes());
        } catch (IOException e) {
            Log.e("FileError", "Export failed: " + e.getMessage());
        }
    }).start();
});

6.3 Testing and Debugging

  • Test CRUD Operations: Add, update, and delete notes to ensure Room works correctly.

  • Verify Theme Persistence: Toggle the theme and restart the app.

  • Check JSON Export: Verify the exported notes.json file contains the correct data.

6.4 Enhancing the App with Advanced Features

  • Add Note Editing: Create a new activity to edit note details.

  • Search Functionality: Add a search bar to filter notes.

  • Cloud Sync: Integrate Firebase for cloud backup.


7. Conclusion and Next Steps

7.1 Recap of Learning Outcomes

You’ve learned how to:

  • Store and retrieve data using SharedPreferences and Room.

  • Parse and display JSON data with Gson.

  • Implement file storage for exporting data.

  • Build a note-taking app with CRUD operations.

No comments:

Post a Comment

Thanks for your valuable comment...........
Md. Mominul Islam

Post Bottom Ad

Responsive Ads Here