Використання наявної БД SQLite

Крім створення нової бази даних ми також можемо використовувати вже наявну. Це може бути більш переважним, тому що в цьому випадку база даних додатка вже міститиме всю необхідну інформацію.

Візьмемо проєкт, створений у попередній темі, де ми мали MainActivity, що виводила список об'єктів, і UserActivity, що давала змогу додавати, редагувати та видаляти об'єкти з БД.

Для початку створимо базу даних SQLite. У цьому нам може допомогти такий інструмент як Sqlitebrowser. Він безкоштовний і доступний для різних операційних систем за адресою https://sqlitebrowser.org/. Хоча можна використовувати й інші способи для створення початкової БД. Краще - tableplus.

Sqlitebrowser представляє графічний інтерфейс для створення бази даних і визначення в ній усіх необхідних таблиць: Як видно на скріншоті, я визначаю таблицю users із трьома полями: _id, name, age. Загальна команда на створення таблиці буде такою:

CREATE TABLE `users` (
    `_id`   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    `name`  TEXT NOT NULL,
    `year`  INTEGER NOT NULL
);

Там же в програмі додамо кілька елементів у створену таблицю:

Після створення таблиці додамо в проєкт в Android Studio папку assets, а в папку assets - щойно створену базу даних. Для цього перейдемо до повного визначення проєкту, натиснемо на папку main правою кнопкою миші і в меню виберемо New -> Directory: Потім у віконці, що з'явилося, виберемо пункт src\main\assets і натиснемо на Enter для її додавання в проєкт: І потім скопіюємо в неї нашу базу даних: У моєму випадку база даних називається "cityinfo.db".

Змінимо код DatabaseHelper таким чином:

package com.example.sqliteapp;
 
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;
import android.util.Log;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_PATH; // повний шлях до бази даних
    private static String DB_NAME = "cityinfo.db";
    private static final int SCHEMA = 1; // версія бази даних
    static final String TABLE = "users"; // назва таблиці в бд
    // назви стовпців
    static final String COLUMN_ID = "_id";
    static final String COLUMN_NAME = "name";
    static final String COLUMN_YEAR = "year";

    private Context myContext;
 
    DatabaseHelper(Context context) {
        super(context, DB_NAME, null, SCHEMA);
        this.myContext=context;
        DB_PATH =context.getFilesDir().getPath() + DB_NAME;
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) { }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion,  int newVersion) { }
 
    void create_db(){
 
        File file = new File(DB_PATH);
        if (!file.exists()) {
            // отримуємо локальну бд як потік
            try(InputStream myInput = myContext.getAssets().open(DB_NAME);
                // Відкриваємо порожню бд
                OutputStream myOutput = new FileOutputStream(DB_PATH)) {
 
                // побайтово копіюємо дані
                byte[] buffer = new byte[1024];
                int length;
                while ((length = myInput.read(buffer)) > 0) {
                    myOutput.write(buffer, 0, length);
                }
                myOutput.flush();
            }
            catch(IOException ex){
                Log.d("DatabaseHelper", ex.getMessage());
            }
        }
    }
    public SQLiteDatabase open()throws SQLException {
 
        return SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);
    }
}

За замовчуванням база даних буде розміщуватися у зовнішньому сховищі, виділеному для додатку в папці /data/data/[назва_пакета]/databases/, і щоб отримати повний шлях до бази даних, у конструкторі використовується вираз:

DB_PATH = context.getFilesDir().getPath() + DB_NAME;

Метод onCreate() нам не потрібен, оскільки нам не потрібно створення вбудованої бази даних. Замість цього тут визначений додатковий метод create_db(), мета якого — копіювання бази даних з папки assets в те місце, яке вказано в змінній DB_PATH.

Окрім того, тут також визначений метод відкриття бази даних open() за допомогою методу SQLiteDatabase.openDatabase().

Новий спосіб організації підключення змінить використання DatabaseHelper в activity. Так, оновимо клас MainActivity:

package com.example.sqliteapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.Intent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.SimpleCursorAdapter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.ListView;
 
public class MainActivity extends AppCompatActivity {
 
    ListView userList;
    DatabaseHelper databaseHelper;
    SQLiteDatabase db;
    Cursor userCursor;
    SimpleCursorAdapter userAdapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        userList = findViewById(R.id.list);
        userList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent(getApplicationContext(), UserActivity.class);
                intent.putExtra("id", id);
                startActivity(intent);
            }
        });
 
        databaseHelper = new DatabaseHelper(getApplicationContext());
        // створюємо базу даних
        databaseHelper.create_db();
    }
 
    @Override
    public void onResume() {
        super.onResume();
        // відкриваємо підключення
        db = databaseHelper.open();
        // отримуємо дані з бд у вигляді курсора
        userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE, null);
        // визначаємо, які стовпці з курсору будуть виводитися в ListView
        String[] headers = new String[]{DatabaseHelper.COLUMN_NAME, DatabaseHelper.COLUMN_YEAR};
        // створюємо адаптер, передаємо в нього курсор
        userAdapter = new SimpleCursorAdapter(this, android.R.layout.two_line_list_item,
                userCursor, headers, new int[]{android.R.id.text1, android.R.id.text2}, 0);
        userList.setAdapter(userAdapter);
    }
 
    // по нажатию на кнопку запускаем UserActivity для добавления данных
    public void add(View view) {
        Intent intent = new Intent(this, UserActivity.class);
        startActivity(intent);
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        // Закрываем подключение и курсор
        db.close();
        userCursor.close();
    }
}

І також змінимо клас UserActivity:

package com.example.sqliteapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
 
public class UserActivity extends AppCompatActivity {
 
    EditText nameBox;
    EditText yearBox;
    Button delButton;
    Button saveButton;
 
    DatabaseHelper sqlHelper;
    SQLiteDatabase db;
    Cursor userCursor;
    long userId = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
 
        nameBox = findViewById(R.id.name);
        yearBox = findViewById(R.id.year);
        delButton = findViewById(R.id.deleteButton);
        saveButton = findViewById(R.id.saveButton);
 
        sqlHelper = new DatabaseHelper(this);
        db = sqlHelper.open();
 
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            userId = extras.getLong("id");
        }
        // якщо 0, то додавання
        if (userId > 0) {
            // отримуємо елемент за id із бд
            userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " +
                    DatabaseHelper.COLUMN_ID + "=?", new String[]{String.valueOf(userId)});
            userCursor.moveToFirst();
            nameBox.setText(userCursor.getString(1));
            yearBox.setText(String.valueOf(userCursor.getInt(2)));
            userCursor.close();
        } else {
            // приховуємо кнопку видалення
            delButton.setVisibility(View.GONE);
        }
    }

    public void save(View view){
        ContentValues cv = new ContentValues();
        cv.put(DatabaseHelper.COLUMN_NAME, nameBox.getText().toString());
        cv.put(DatabaseHelper.COLUMN_YEAR, Integer.parseInt(yearBox.getText().toString()));
 
        if (userId > 0) {
            db.update(DatabaseHelper.TABLE, cv, DatabaseHelper.COLUMN_ID + "=" + userId, null);
        } else {
            db.insert(DatabaseHelper.TABLE, null, cv);
        }
        goHome();
    }

    public void delete(View view){
        db.delete(DatabaseHelper.TABLE, "_id = ?", new String[]{String.valueOf(userId)});
        goHome();
    }

    private void goHome(){
        // закриваємо підключення
        db.close();
        // переход к главной activity
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(intent);
    }
}

Уся інша робота з даними буде тією ж, щоб і в минулих темах: