Введение
Есть два подхода к работе с БД в Android приложениях.
В первом варианте БД создается в событии OnCreate главной активности. Данный вариант хорош для случая, когда база данных при установке приложения пуста либо заполнена небольшим количеством данных, а также в БД активно производятся записи в дальнейшем.
Но данный способ не очень хорош, если, например, пишите какой-нибудь справочник или другое приложение, когда БД при установке приложения уже должна быть заполнена большим количеством записей. На мой взгляд, в этом случае лучше БД подготовить заранее, а потом уже её подключить как отдельный файл в ресурсах приложения. В данной статье рассмотрен данный случай.
Adding test.db file into the project
Right click on app folder goto New> Folder>Assets Folder
Keep all the settings default on Dialog box. Under target source set, option main should be selected and Click Finish.
Now open app folder. you will find your Assets folder by the name of “assets”. Copy file test.db to assets folder.
Create DatabaseHelper class to the following
Create activity
Change the
activity_main.xml
layout to the following.
Creating the project
Open the Android Studio, then click “Start a new Android Studio project”.
Enter the info below:
Application Name: LoginWithSQLiteDBExample
Overview
In this tutorial, we show you how to create Android Login Example with SQLite Database using Android Studio. This Application has two screens, first one is login screen where we can just login with our credentials. Second screen is Welcome screen after we login successfully.
Run this app in the android emulator
You can run this app from an Android Studio project. Or you can run this
app that’s been installed on the emulator as you would run any app on a
device.
To start the emulator and run this app in your project
Note
Sql lite android app login authentication from database
I have a Login Activity with Name and password fields and a SignUp Activity, I want to authenticate the name and password from database, I have created a database and insert values in database using SignUp activity, which was successful, but for Login Activity when I click the login button my app crashes, I have tried many methods but all in vain.
Login activity
public class LoginActivity extends AppCompatActivity {
DatabaseHelper myDb;
String name;
String password;
boolean storedPassword;
private static final String TAG = "LoginActivity";
private static final int REQUEST_SIGNUP = 0;
@Bind(R.id.input_name1)
EditText _nameText;
@Bind(R.id.input_password)
EditText _passwordText;
@Bind(R.id.btn_login)
Button _loginButton;
@Bind(R.id.link_signup)
TextView _signupLink;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
_loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
_signupLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Start the Signup activity
Intent intent = new Intent(getApplicationContext(), SignupActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
}
public void login() {
Log.d(TAG, "Login");
if (!validate()) {
onLoginFailed();
return;
}
_loginButton.setEnabled(false);
final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this,
R.style.AppTheme_Dark_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
name = _nameText.getText().toString();
password = _passwordText.getText().toString();
// TODO: Implement your own authentication logic here.
storedPassword = myDb.getData(name, password);
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
// On complete call either onLoginSuccess or onLoginFailed
if (storedPassword = true) {
onLoginSuccess();
} else {
onLoginFailed();
}
progressDialog.dismiss();
}
}, 3000);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SIGNUP) {
if (resultCode == RESULT_OK) {
// TODO: Implement successful signup logic here
// By default we just finish the Activity and log them in automatically
this.finish();
}
}
}
@Override
public void onBackPressed() {
// Disable going back to the MainActivity
moveTaskToBack(true);
}
public void onLoginSuccess() {
_loginButton.setEnabled(true);
Toast.makeText(getBaseContext(), "Login Success", Toast.LENGTH_LONG).show();
}
public void onLoginFailed() {
Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();
_loginButton.setEnabled(true);
}
public boolean validate() {
boolean valid = true;
String email = _nameText.getText().toString();
String password = _passwordText.getText().toString();
if (email.isEmpty()) {
_nameText.setError("enter a valid name");
valid = false;
} else {
_nameText.setError(null);
}
if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
_passwordText.setError("between 4 and 10 alphanumeric characters");
valid = false;
} else {
_passwordText.setError(null);
}
return valid;
}
}
Database
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "User.db";
public static final String TABLE_NAME = "user_table";
public static final String COL_1 = "ID";
public static final String COL_2 = "NAME";
public static final String COL_3 = "EMAIL";
public static final String COL_4 = "PASSWORD";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table " TABLE_NAME " (ID INTEGER PRIMARY KEY AUTOINCREMENT,NAME TEXT,EMAIL TEXT,PASSWORD INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " TABLE_NAME);
onCreate(db);
}
public boolean insertData(String name,String email,String number) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COL_2,name);
contentValues.put(COL_3,email);
contentValues.put(COL_4, number);
long result = db.insert(TABLE_NAME,null ,contentValues);
if(result == -1)
return false;
else
return true;
}
public boolean getData(String name, String password){
SQLiteDatabase db = this.getReadableDatabase();
Cursor mCursor = db.rawQuery("SELECT * FROM " TABLE_NAME " WHERE NAME=? AND PASSWORD=?",
new String[]{name,password});
if (mCursor != null) {
if(mCursor.getCount() > 0)
{
mCursor.close();
return true;
}
}
return false;
}
}
Log Cat
FATAL EXCEPTION: main
Process: com.sourcey.materialloginexample, PID: 2131
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.sourcey.materiallogindemo.DatabaseHelper.getData(java.lang.String, java.lang.String)' on a null object reference
at com.sourcey.materiallogindemo.LoginActivity.login(LoginActivity.java:83)
at com.sourcey.materiallogindemo.LoginActivity$1.onClick(LoginActivity.java:47)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Sqlite creating database and table
Create Database Name:
test.db
Добавление бд в проект
Все приготовления сделаны. Теперь можем начать работать по теме статьи. Вначале добавим файл базы данных в проект.
Создадим папку assets в нашем проекте:
Скопируем файл нашей базы данных:
Добавление класса для работы с бд
Для открытия и подготовки БД в Android используется наследник класса SQLiteOpenHelper. Мы тоже создадим наследник этого класса DatabaseHelper, но он будет сильно модифицированный, так как мы будем работать с готовой базой данных, а не создавать ей с помощью SQL запросов:
Ниже приведен текст всего класса, который нужно просто скопировать (не трогая свой первой строчки package):
importandroid.content.Context;importandroid.database.SQLException;importandroid.database.sqlite.SQLiteDatabase;importandroid.database.sqlite.SQLiteOpenHelper;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;publicclassDatabaseHelperextendsSQLiteOpenHelper{privatestaticStringDB_NAME="info.db";privatestaticStringDB_PATH="";privatestaticfinalintDB_VERSION=1;privateSQLiteDatabasemDataBase;privatefinalContextmContext;privatebooleanmNeedUpdate=false;publicDatabaseHelper(Contextcontext){super(context,DB_NAME,null,DB_VERSION);if(android.os.Build.VERSION.SDK_INT>=17)DB_PATH=context.getApplicationInfo().dataDir "/databases/";elseDB_PATH="/data/data/" context.getPackageName() "/databases/";this.mContext=context;copyDataBase();this.getReadableDatabase();}publicvoidupdateDataBase()throwsIOException{if(mNeedUpdate){FiledbFile=newFile(DB_PATH DB_NAME);if(dbFile.exists())dbFile.delete();copyDataBase();mNeedUpdate=false;}}privatebooleancheckDataBase(){FiledbFile=newFile(DB_PATH DB_NAME);returndbFile.exists();}privatevoidcopyDataBase(){if(!checkDataBase()){this.getReadableDatabase();this.close();try{copyDBFile();}catch(IOExceptionmIOException){thrownewError("ErrorCopyingDataBase");}}}privatevoidcopyDBFile()throwsIOException{InputStreammInput=mContext.getAssets().open(DB_NAME);OutputStreammOutput=newFileOutputStream(DB_PATH DB_NAME);byte[]mBuffer=newbyte[1024];intmLength;while((mLength=mInput.read(mBuffer))>0)mOutput.write(mBuffer,0,mLength);mOutput.flush();mOutput.close();mInput.close();}publicbooleanopenDataBase()throwsSQLException{mDataBase=SQLiteDatabase.openDatabase(DB_PATH DB_NAME,null,SQLiteDatabase.CREATE_IF_NECESSARY);returnmDataBase!=null;}@Overridepublicsynchronizedvoidclose(){if(mDataBase!=null)mDataBase.close();super.close();}@OverridepublicvoidonCreate(SQLiteDatabasedb){}@OverridepublicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){if(newVersion>oldVersion)mNeedUpdate=true;}}
Для работы в последующим с другими базами данных вам ничего не нужно будет менять в данном классе, кроме строчек:
Разберем что означают эти строчки.
DB_NAME — имя файла БД. Какой файл БД вы создали, такое название сюда и копируем.
DB_PATH — путь к БД. Каждое приложение в Android имеет свою область памяти, куда складываются файлы программы. Вдруг вы захотите вывернуть путь к файлу БД. Я бы ничего не трогал.
DB_VERSION — самая интересная переменная (причем в примерах в сети по работе с готовой БД её обходят стороной). Это номер версии БД. Ниже описан принцип работы данного класса. Например, вы пишите справочник рецептов под Android и рецепты храните в БД.
В момент создания установки приложения программа должна скопировать БД на устройство. Потом через какое-то время вы решили обновить приложение, и БД у вас обновилась: структура БД поменялась, добавились новые рецепты. И вам нужно заменить старую БД на новую.
Итак, логика работы класса DatabaseHelper в подготовке базы данных:
Копируем файл БД, если этого файла нет (при установке приложения).
Если номер БД обновлен, то заменяем один файл базы данных на другой:
Добавление новых записей из android приложения
В предыдущей версии статьи многие спрашивали, а как добавлять новые записи в таблицу. Абсолютно также, как и в обычном подходе в работе с базой данных. Например, вот код добавления новой записи:
И полный код примера файла активности с данным кодом (без строки package):
importandroidx.appcompat.app.AppCompatActivity;importandroid.database.Cursor;importandroid.database.SQLException;importandroid.database.sqlite.SQLiteDatabase;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importandroid.widget.ListView;importandroid.widget.SimpleAdapter;importandroid.widget.TextView;importjava.io.IOException;importjava.util.ArrayList;importjava.util.HashMap;publicclassMainActivityextendsAppCompatActivity{// Объявим переменные компонентовButtonbutton;TextViewtextView;// Переменная для работы с БДprivateDatabaseHelpermDBHelper;privateSQLiteDatabasemDb;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mDBHelper=newDatabaseHelper(this);try{mDBHelper.updateDataBase();}catch(IOExceptionmIOException){thrownewError("UnableToUpdateDatabase");}try{mDb=mDBHelper.getWritableDatabase();}catch(SQLExceptionmSQLException){throwmSQLException;}// Найдем компоненты в XML разметкеbutton=(Button)findViewById(R.id.button);textView=(TextView)findViewById(R.id.textView);// Пропишем обработчик клика кнопкиbutton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){Stringquery="INSERT INTO clients (name, age) VALUES ('Tom', '11')";mDb.execSQL(query);updateList();}});updateList();}voidupdateList(){// Список клиентовArrayList<HashMap<String,Object>>clients=newArrayList<HashMap<String,Object>>();// Список параметров конкретного клиентаHashMap<String,Object>client;// Отправляем запрос в БДCursorcursor=mDb.rawQuery("SELECT * FROM clients",null);cursor.moveToFirst();// Пробегаем по всем клиентамwhile(!cursor.isAfterLast()){client=newHashMap<String,Object>();// Заполняем клиентаclient.put("name",cursor.getString(1));client.put("age",cursor.getString(2));// Закидываем клиента в список клиентовclients.add(client);// Переходим к следующему клиентуcursor.moveToNext();}cursor.close();// Какие параметры клиента мы будем отображать в соответствующих// элементах из разметки adapter_item.xmlString[]from={"name","age"};int[]to={R.id.textView,R.id.textView2};// Создаем адаптерSimpleAdapteradapter=newSimpleAdapter(this,clients,R.layout.adapter_item,from,to);ListViewlistView=(ListView)findViewById(R.id.listView);listView.setAdapter(adapter);}}
Но я крайне не рекомендую добавлять новые записи в таблицу из-под Android приложения, если база данных представлена в виде файла. Так как если вы потом обновите файл базы данных в другой версии вашего приложения, то пользователь потеряет все свои данные.
Либо пропишите бэкап пользовательских записей, либо используете для них вторую базу данных, созданную стандартным способом.<ul><li>Android Studio icon.svg by Google Inc. / (2022-06-07)</li><li>Sqlite-square-icon.svg by Mike Toews / (2022-01-26)</li></ul>
Обновление бд
Откроем в программе DB Browser for SQLite файл БД, который располагается в папке assets нашей программы. И внесем какие-нибудь изменения и сохраним. Я для примера поменял имя в первой строке таблицы:
Итак, в исходниках программы у нас файл БД поменялся. Запустим приложение.
И увидим, что в приложении изменения не проявились. Почему? Потому что приложение не обновляет файл БД каждый раз при запуске приложения. А вдруг вы записываете в БД какие-то записи: тогда при обновлении файла все добавленные записи сотрутся:
Нам нужно в файле класса DatabaseHelper поменять номер версии БД в сторону увеличения:
Теперь при запуске приложения данные обновятся:
Обратите внимание на то, что обновление БД произойдет только один раз. И до следующего изменения переменной DB_VERSION файл БД обновляться файлом из папки assets не будет.
Внимание! При обновлении БД заменяется файл БД, а, значит, все внесенные изменения в БД на приложении в Android (через запросы INSERT, UPDATE) будут удалены! Поэтому, если вам внесенные изменения нужны, то не вызывайте метод updateDataBase, а в методе onUpgrade внесите стандартным способом обновления в БД. При этом замена файла БД не будет происходить. Например, так можно вставить в таблицу новый столбец:
Отображение списка данных по запросу
В примерах выше мы выводили информацию просто в строку в textView. Для демонстрации работы класса это достаточно, но чаще всего требуется данные из БД выводить списком. Напоследок приведу пример, когда на экран список людей из таблицы БД выведется не в строчку, а списком.
Делается это через обычный адаптер. Особенно не буду перегружать объяснением.
В activity_main.xml добавляем ListView:
Создадим файл разметки adapter_item.xml, в котором опишем внешний вид одного элемента списка с таким содержанием:
Добавим, например, в метод onCreate главной активности код:
// Список клиентовArrayList<HashMap<String,Object>>clients=newArrayList<HashMap<String,Object>>();// Список параметров конкретного клиентаHashMap<String,Object>client;// Отправляем запрос в БДCursorcursor=mDb.rawQuery("SELECT * FROM clients",null);cursor.moveToFirst();// Пробегаем по всем клиентамwhile(!cursor.isAfterLast()){client=newHashMap<String,Object>();// Заполняем клиентаclient.put("name",cursor.getString(1));client.put("age",cursor.getString(2));// Закидываем клиента в список клиентовclients.add(client);// Переходим к следующему клиентуcursor.moveToNext();}cursor.close();// Какие параметры клиента мы будем отображать в соответствующих// элементах из разметки adapter_item.xmlString[]from={"name","age"};int[]to={R.id.textView,R.id.textView2};// Создаем адаптерSimpleAdapteradapter=newSimpleAdapter(this,clients,R.layout.adapter_item,from,to);ListViewlistView=(ListView)findViewById(R.id.listView);listView.setAdapter(adapter);
Запускаем приложение. Видим список наших клиентов:
Помните, что запросы к БД могут быть длительными, поэтому работу с БД лучше запихивать в другой поток, например, через ASyncTask.
Подготовка java кода
Нам потребуется обработать клик на кнопку button и что-то записать в textView.
Поэтому найдем данные компоненты и свяжем их в Java коде с XML:
Объявим переменные компонентов:
Найдем компоненты в XML разметке:
Пропишем обработчик клика кнопки:
Полный код Java файла (без строчки package, которая у вас должна быть своей):
Подключаемся к бд
Перейдем в класс нашей активности. В нем создадим экземпляр класса DatabaseHelper, попытаемся обновить БД, если это требуется, а потом вытащим экземпляр SQLiteDatabase.
Создадим переменные в классе:
В методе onCreate выполним подготовительные действия:
Работа с базой данных
Теперь мы можем наконец в клике кнопки соединиться с базой данной и вытащить нужные нам данные.
Давайте при клике кнопки в textView отобразятся все имена учеников в строчку. Будем работать с помощью Cursor.
В setOnClickListener припишем такой, например, код:
Полный код Java файла активности у меня получился такой (без первой строчки package):
Запустим приложение:
Вот так приложение выглядит при запуске:
При нажатии на кнопку получим список имен из БД:
Фактически это всё. У нас есть экземпляр SQLiteDatabase mDb, с которым мы можем работать так как нам нужно. Дальше будут рассмотрены некоторые особенности работы с БД.
Работа с большой бд
Когда готовил статью, то я часто встречал замечания, что файлы больше 1 Мб или 8 Мб из папки assets не копируются. Хотя, я пробовал работать с файлом в 14 Мб. Запускал на разных устройствах и никаких проблем не заметил.
Если что, то вот этот файл info_large.zip
Но мало ли. Вдруг у вас проблемы будут замечены. В качестве решения можно размещать файл БД не папке assets, а в папке res/raw:
И файл БД копируем в эту папку:
В классе DatabaseHelper нам нужно поменять только одну строчку в методе copyDBFile.
Было:
Стало:
Да, если вы перед этим использовали старый вариант на том устройстве, где тестируете приложение, то не забудьте поменять версию базы данных DB_VERSION, а то вы не увидите изменений в базе данных. Можно также удалить приложение вначале, а заново его установить.
Полный код класса (без строчки package):
importandroid.content.Context;importandroid.database.SQLException;importandroid.database.sqlite.SQLiteDatabase;importandroid.database.sqlite.SQLiteOpenHelper;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;publicclassDatabaseHelperextendsSQLiteOpenHelper{privatestaticStringDB_NAME="info.db";privatestaticStringDB_PATH="";privatestaticfinalintDB_VERSION=1;privateSQLiteDatabasemDataBase;privatefinalContextmContext;privatebooleanmNeedUpdate=false;publicDatabaseHelper(Contextcontext){super(context,DB_NAME,null,DB_VERSION);if(android.os.Build.VERSION.SDK_INT>=17)DB_PATH=context.getApplicationInfo().dataDir "/databases/";elseDB_PATH="/data/data/" context.getPackageName() "/databases/";this.mContext=context;copyDataBase();this.getReadableDatabase();}publicvoidupdateDataBase()throwsIOException{if(mNeedUpdate){FiledbFile=newFile(DB_PATH DB_NAME);if(dbFile.exists())dbFile.delete();copyDataBase();mNeedUpdate=false;}}privatebooleancheckDataBase(){FiledbFile=newFile(DB_PATH DB_NAME);returndbFile.exists();}privatevoidcopyDataBase(){if(!checkDataBase()){this.getReadableDatabase();this.close();try{copyDBFile();}catch(IOExceptionmIOException){thrownewError("ErrorCopyingDataBase");}}}privatevoidcopyDBFile()throwsIOException{//InputStream mInput = mContext.getAssets().open(DB_NAME);InputStreammInput=mContext.getResources().openRawResource(R.raw.info_large);OutputStreammOutput=newFileOutputStream(DB_PATH DB_NAME);byte[]mBuffer=newbyte[1024];intmLength;while((mLength=mInput.read(mBuffer))>0)mOutput.write(mBuffer,0,mLength);mOutput.flush();mOutput.close();mInput.close();}publicbooleanopenDataBase()throwsSQLException{mDataBase=SQLiteDatabase.openDatabase(DB_PATH DB_NAME,null,SQLiteDatabase.CREATE_IF_NECESSARY);returnmDataBase!=null;}@Overridepublicsynchronizedvoidclose(){if(mDataBase!=null)mDataBase.close();super.close();}@OverridepublicvoidonCreate(SQLiteDatabasedb){}@OverridepublicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){if(newVersion>oldVersion)mNeedUpdate=true;}}
Разметка активности
Так как мы создаем простейшее приложение, но в XML файле активности разместим только кнопку и поле для вывода текста:
Создание android проекта
Открываем Android Studio и создаем там новый проект с пустой активностью. Всё как обычно:
Создание базы данных
Для создания БД SQLite будем использовать, например, DB Browser for SQLite. Скачиваем и устанавливаем.
Буем создавать БД с одной таблицей такого вида.
_id | name | age |
---|---|---|
1 | Anton | 30 |
2 | Alina | 24 |
3 | Dima | 28 |
4 | Dasha | 23 |
Итак, создаем базу данных:
Где-нибудь сохраняем и называем, например, info.db:
Создаем таблицу, например, clients. И добавляем там поле:
Первым полем у нас будет номер записи _id. Поле будет также первичным ключом:
Аналогичным способом создаем поля age и name. И жмем OK:
В списке таблиц у нас появилась наша таблица clients:
Переходим в режим заполнения таблицы:
Выбираем там нашу таблицу и жмем Добавить запись:
Заполняем наши данные и сохраняем изменения в БД:
Файл подготовленной базы данных можно взять из архива: info.zip.
Создание и открытие базы данных
Для создания или открытия новой базы данных из кода Activity в Android мы можем вызвать метод openOrCreateDatabase(). Этот метод
может принимать три параметра:
название для базы данных
числовое значение, которое определяет режим работы (как правило, в виде константы
MODE_PRIVATE
)необязательный параметр в виде объекта
SQLiteDatabase.CursorFactory
, который представляет фабрику создания курсора для работы с бд
Например, создание базы данных app.db:
SQLiteDatabase db = getBaseContext().openOrCreateDatabase("app.db", MODE_PRIVATE, null);