Создание облачного бэкенда для вашего Android-приложения с использованием Firebase
Этот пост был обновлен в сентябре 2016 года, чтобы отразить изменения в платформе Firebase .
18 месяцев назад я написал статью о создании облачного бэкенда для вашего Android-приложения с помощью Parse . Ранее в этом году Facebook объявил, что 28 января 2017 года они закрывают Parse , и новые подключения к службе невозможны. С этим неожиданным объявлением разработчики, полагающиеся на сервис, начали искать альтернативы, на которые они могли бы перенести свои данные .
Из-за этой новости я подумал, что должен вернуться к этой теме и рассказать о том, как использовать другой Backend в качестве сервисной платформы для управления данными для вашего приложения Android.
В этом руководстве я расскажу о Firebase , популярной серверной платформе, приобретенной Google в октябре 2014 года.
Вы всегда должны взвесить все за и против, полагаясь на BaaS, а не на создание собственного. Parse не первая платформа BaaS, которая закрывается (например, StackMob ) и не будет последней. Как разработчик, опирающийся на одну из этих платформ, вы всегда должны быть готовы к миграции и иметь план резервного копирования.
С помощью Firebase вы можете хранить и синхронизировать данные в облачной базе данных NoSQL. Данные хранятся в формате JSON, синхронизируются со всеми подключенными клиентами в режиме реального времени и доступны, когда ваше приложение отключается. Он предлагает API-интерфейсы, которые позволяют аутентифицировать пользователей с помощью электронной почты и пароля, Facebook, Twitter, GitHub, Google, анонимной аутентификации или интеграции с существующей системой аутентификации. Помимо базы данных в реальном времени и аутентификации, он предлагает множество других услуг, включая облачные сообщения, хранилище, хостинг, удаленную настройку, тестовую лабораторию, отчеты о сбоях, уведомления, индексацию приложений, динамические ссылки, приглашения, AdWords, AdMob.
В этой статье вы создадите простое приложение To Do, которое покажет, как сохранять и извлекать данные из Firebase, как проверять подлинность пользователей, устанавливать разрешения на чтение / запись для данных и проверять данные на сервере.
Вы можете найти код для этого проекта на GitHub .
Настройка проекта
Для начала создайте новый проект с именем To Do. Установите Минимальный SDK как API 15 в следующем окне и выберите Основные действия в следующем. Нажмите Finish в последнем окне, оставив настройки по умолчанию.
Перед началом проекта Android зайдите на firebase.google.com и создайте аккаунт. После входа в свою учетную запись перейдите на консоль Firebase и создайте проект, в котором будут храниться данные вашего приложения.
Введите название и страну / регион для проекта.
Страна / регион представляет страну / регион вашей организации / компании. Ваш выбор также устанавливает соответствующую валюту для отчетов о доходах. После задания имени (я использовал SPToDoApp ) и региона для проекта, нажмите кнопку «Создать проект». Это создаст проект, и откроется его консоль. В консоли проекта нажмите кнопку « Добавить Firebase в приложение для Android» .
Введите имя пакета вашего проекта Android в появившемся окне. Если ваше приложение будет использовать определенные сервисы Google Play, такие как Google Sign-In, App Invites, Dynamic Links и т. Д., Вам придется предоставить SHA-1 вашего сертификата подписи. Это приложение не будет использовать ни одну из этих служб, поэтому оставьте это поле пустым. Если вы хотите добавить эти сервисы в свое приложение, перейдите на эту страницу для получения информации об использовании keytool, чтобы получить хэш SHA-1 вашего сертификата подписи. На странице содержатся инструкции по получению отпечатков сертификатов выпуска и отладки. Когда закончите, нажмите на кнопку Добавить приложение .
Нажмите « Добавить приложение», чтобы загрузить файл google-services.json на ваш компьютер. На следующей странице диалогового окна приведены инструкции по размещению загруженного файла JSON. Найдите загруженный файл и переместите его в корневой каталог модуля приложения вашего проекта Android.
Файл JSON содержит параметры конфигурации, необходимые приложению Android для взаимодействия с серверами Firebase. Он содержит такие детали, как URL-адрес проекта Firebase, ключ API и т. Д. В предыдущей версии Firebase вам приходилось хранить их вручную в коде приложения, но теперь этот процесс был упрощен благодаря использованию одного файла, который содержит необходимые данные.
Если вы используете контроль версий и храните свой код в общедоступном репозитории, вам следует рассмотреть возможность размещения файла google-services.json в файле .gitignore , чтобы эта информация не была общедоступной.
Когда закончите, нажмите кнопку « Продолжить» в диалоговом окне, и вы получите дальнейшие инструкции по настройке.
В свой файл build.gradle уровня проекта добавьте следующее в узел buildscript> зависимости . Это сделано для включения подключаемого модуля служб Google для Gradle, который загружает загруженный файл google-services.json .
classpath 'com.google.gms:google-services:3.0.0'
Убедитесь, что вы редактируете правильный файл Gradle.
Затем в файле build.gradle уровня приложения добавьте следующее в нижней части файла, чтобы включить плагин Gradle.
apply plugin: 'com.google.gms.google-services'
Опять же, убедитесь, что это правильный файл build.gradle .
Затем добавьте следующие зависимости в тот же файл. Firebase имеет различные SDK для своих функций. Здесь вы добавляете библиотеку, необходимую для использования базы данных реального времени, и библиотеку, необходимую для аутентификации.
compile 'com.google.firebase:firebase-database:9.4.0' compile 'com.google.firebase:firebase-auth:9.4.0'
Создайте пустое действие, выбрав пункт меню Файл -> Создать -> Деятельность -> Пустое действие и назовите его LogInActivity
. Создайте другое и назовите его SignUpActivity
.
Измените содержимое файла макета activity_log_in.xml на:
<?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:paddingBottom = "@dimen/activity_vertical_margin" android:paddingLeft = "@dimen/activity_horizontal_margin" android:paddingRight = "@dimen/activity_horizontal_margin" android:paddingTop = "@dimen/activity_vertical_margin" tools:context = ".LoginActivity" > < EditText android:id = "@+id/emailField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignParentLeft = "true" android:layout_alignParentTop = "true" android:ems = "10" android:inputType = "textEmailAddress" android:hint = "@string/email_hint" > < requestFocus /> </ EditText > < EditText android:id = "@+id/passwordField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignLeft = "@+id/emailField" android:layout_below = "@+id/emailField" android:ems = "10" android:hint = "@string/password_hint" android:inputType = "textPassword" /> < Button android:id = "@+id/loginButton" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignLeft = "@+id/passwordField" android:layout_below = "@+id/passwordField" android:text = "@string/login_button_label" /> < TextView android:id = "@+id/signUpText" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_below = "@+id/loginButton" android:layout_centerHorizontal = "true" android:layout_marginTop = "69dp" android:text = "@string/sign_up_text" /> </ RelativeLayout >
Измените содержимое файла макета activity_sign_up.xml на:
<?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:paddingBottom = "@dimen/activity_vertical_margin" android:paddingLeft = "@dimen/activity_horizontal_margin" android:paddingRight = "@dimen/activity_horizontal_margin" android:paddingTop = "@dimen/activity_vertical_margin" tools:context = ".SignUpActivity" > < EditText android:id = "@+id/emailField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignParentLeft = "true" android:layout_alignParentTop = "true" android:ems = "10" android:inputType = "textEmailAddress" android:hint = "@string/email_hint" > < requestFocus /> </ EditText > < EditText android:id = "@+id/passwordField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignLeft = "@+id/emailField" android:layout_below = "@+id/emailField" android:ems = "10" android:inputType = "textPassword" android:hint = "@string/password_hint" /> < Button android:id = "@+id/signupButton" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignLeft = "@+id/passwordField" android:layout_below = "@+id/passwordField" android:text = "@string/sign_up_button_label" /> </ RelativeLayout >
Эти макеты предназначены для представлений входа в систему и регистрации.
Добавьте следующее в values / strings.xml .
< string name = "password_hint" > Password </ string > < string name = "email_hint" > Email </ string > < string name = "sign_up_button_label" > Sign Up </ string > < string name = "signup_error_message" > Please make sure you enter an email address and password! </ string > < string name = "signup_error_title" > Error! </ string > < string name = "signup_success" > Account successfully created! You can now Login. </ string > < string name = "login_error_message" > Please make sure you enter an email address and password! </ string > < string name = "login_error_title" > Error! </ string > < string name = "login_button_label" > Login </ string > < string name = "sign_up_text" > Sign Up! </ string > < string name = "title_activity_login" > Sign in </ string > < string name = "add_item" > Add New Item </ string > < string name = "action_logout" > Logout </ string >
В файле activity_main.xml удалите разметку FloatingActionButton
.
< android.support.design.widget.FloatingActionButton android:id = "@+id/fab" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_gravity = "bottom|end" android:layout_margin = "@dimen/fab_margin" android:src = "@android:drawable/ic_dialog_email" />
И в MainActivity.java удалите там и код FAB.
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener( new View.OnClickListener() { @Override public void onClick (View view) { Snackbar.make(view, "Replace with your own action" , Snackbar.LENGTH_LONG) .setAction( "Action" , null ).show(); } });
В res / menu / menu_main.xml замените элемент настроек следующим элементом Logout.
< item android:id = "@+id/action_logout" android:orderInCategory = "100" android:title = "@string/action_logout" app:showAsAction = "never" />
Откройте MainActivity
и замените идентификатор action_logout
идентификатором onOptionsItemSelected(MenuItem)
в onOptionsItemSelected(MenuItem)
.
if (id == R.id.action_logout) { return true ; }
Этот пункт меню выйдет из системы пользователя.
Измените content_main.xml на следующий:
<?xml version="1.0" encoding="utf-8"?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" xmlns:app = "http://schemas.android.com/apk/res-auto" android:layout_width = "match_parent" android:layout_height = "match_parent" android:paddingLeft = "@dimen/activity_horizontal_margin" android:paddingRight = "@dimen/activity_horizontal_margin" android:paddingTop = "@dimen/activity_vertical_margin" android:paddingBottom = "@dimen/activity_vertical_margin" app:layout_behavior = "@string/appbar_scrolling_view_behavior" tools:context = ".MainActivity" tools:showIn = "@layout/activity_main" android:orientation = "vertical" > < ListView android:id = "@+id/listView" android:layout_width = "match_parent" android:layout_height = "0dp" android:layout_weight = "1" > </ ListView > < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "vertical" android:layout_gravity = "bottom" > < EditText android:id = "@+id/todoText" android:layout_width = "match_parent" android:layout_height = "wrap_content" /> < Button android:id = "@+id/addButton" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "@string/add_item" /> </ LinearLayout > </ LinearLayout >
Activity_main.xml имеет тег include
который указывает и загружает content_main.xml . Приложение отобразит элементы списка дел в виде списка, который займет большую часть экрана. В нижней части экрана будет поле для редактирования текста и кнопка для добавления элементов в список.
После настройки пользовательского интерфейса приложения вы узнаете, как сохранять и извлекать данные в Firebase и из него.
Безопасность и правила
Прежде чем извлекать и сохранять данные с сервера Firebase, вам нужно настроить аутентификацию и добавить правила, которые ограничивают доступ к данным и проверяют ввод пользователя перед его сохранением.
Аутентификация
Firebase API имеет встроенные методы для электронной почты / пароля, Facebook, Twitter, GitHub, Google и анонимной аутентификации. В этом руководстве будет использоваться аутентификация по электронной почте и паролю.
Перейдите к консоли Firebase и откройте панель управления вашего проекта, нажав на проект.
Если вы выберете « База данных» на левой панели, вы сможете увидеть данные проекта в формате JSON.
Firebase хранит все данные базы данных в виде объектов JSON. Там нет таблиц или записей. Когда вы добавляете данные в дерево JSON, оно становится ключом в существующей структуре JSON.
На данный момент вы можете видеть только корневой узел.
Если вы наведите курсор мыши на узел, вы увидите элементы управления + и x , которые можно использовать для добавления данных в дерево и удаления этого узла соответственно.
На левой панели нажмите « Auth» и затем выберите вкладку « Sign In Method » справа. Включить аутентификацию по электронной почте / паролю от указанных провайдеров.
В Android Studio добавьте следующий метод в MainActivity
.
private void loadLogInView () { Intent intent = new Intent( this , LogInActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); }
Это переходит к представлению Login
в Login
и очищает стек активности. Это не позволяет пользователю вернуться к основному виду деятельности, когда он нажимает кнопку « Назад» в представлении входа в систему.
Добавьте следующие переменные в класс.
private FirebaseAuth mFirebaseAuth; private FirebaseUser mFirebaseUser;
Затем добавьте следующее в onCreate()
.
// Initialize Firebase Auth mFirebaseAuth = FirebaseAuth.getInstance(); mFirebaseUser = mFirebaseAuth.getCurrentUser(); if (mFirebaseUser == null ) { // Not logged in, launch the Log In activity loadLogInView(); }
Здесь вы проверяете вошедшего в систему пользователя. Если пользователь не вошел в систему, getCurrentUser()
вернет значение null
, в противном случае он вернет объект FirebaseUser
который будет содержать сведения о вошедшем в систему пользователе. Если пользователь не вошел в систему, loadLogInView()
который перенаправляет пользователя в представление входа в систему.
Запустите приложение, и вы будете перенаправлены на страницу входа.
Добавьте следующее в LogInActivity
.
protected EditText emailEditText; protected EditText passwordEditText; protected Button logInButton; protected TextView signUpTextView; private FirebaseAuth mFirebaseAuth;
Измените метод onCreate()
класса LogInActivity
на:
@Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_log_in); // Initialize FirebaseAuth mFirebaseAuth = FirebaseAuth.getInstance(); signUpTextView = (TextView) findViewById(R.id.signUpText); emailEditText = (EditText) findViewById(R.id.emailField); passwordEditText = (EditText) findViewById(R.id.passwordField); logInButton = (Button) findViewById(R.id.loginButton); signUpTextView.setOnClickListener( new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(LogInActivity. this , SignUpActivity.class); startActivity(intent); } }); logInButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick (View v) { String email = emailEditText.getText().toString(); String password = passwordEditText.getText().toString(); email = email.trim(); password = password.trim(); if (email.isEmpty() || password.isEmpty()) { AlertDialog.Builder builder = new AlertDialog.Builder(LogInActivity. this ); builder.setMessage(R.string.login_error_message) .setTitle(R.string.login_error_title) .setPositiveButton(android.R.string.ok, null ); AlertDialog dialog = builder.create(); dialog.show(); } else { mFirebaseAuth.signInWithEmailAndPassword(email, password) .addOnCompleteListener(LogInActivity. this , new OnCompleteListener<AuthResult>() { @Override public void onComplete (@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { Intent intent = new Intent(LogInActivity. this , MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } else { AlertDialog.Builder builder = new AlertDialog.Builder(LogInActivity. this ); builder.setMessage(task.getException().getMessage()) .setTitle(R.string.login_error_title) .setPositiveButton(android.R.string.ok, null ); AlertDialog dialog = builder.create(); dialog.show(); } } }); } } }); }
Это инициирует элементы представления и объект FirebaseAuth
, который является точкой входа в Firebase Authentication SDK. Прослушиватель событий добавляется в текстовое представление «Регистрация», при нажатии которого открывается действие «Регистрация». Другой прослушиватель событий на кнопке входа в систему выполняет проверку ввода данных пользователем, обеспечивая ввод текста пользователем в оба поля. Затем он вызывает сервер Firebase с помощью signInWithEmailAndPassword()
. Функция принимает электронную почту и пароль пользователя и возвращает Task
AuthResult
Вы проверяете, успешно ли Task
выполнено, и перенаправляете пользователя в MainActivity, в противном случае выводите им сообщение об ошибке. Ниже приведено сообщение об ошибке, полученное при попытке войти в систему пользователя, который не зарегистрирован.
Возможно, вы захотите показать пользователю лучшее сообщение, чем возвращаемое task.getException().getMessage()
в вашем приложении. Вы можете проверить наличие исключения, чтобы определить сообщение об ошибке, чтобы показать пользователю. Вы можете получить одно из следующих исключений при сбое аутентификации:
- FirebaseAuthInvalidUserException выдается, если учетная запись пользователя, соответствующая электронной почте, не существует или отключена.
- FirebaseAuthInvalidCredentialsException выдается, если пароль неверный.
Помимо signInWithEmailAndPassword()
, вы можете использовать следующее для входа в систему:
- signInWithCredential (AuthCredential) — Пытается войти в систему пользователя с заданным AuthCredential . Используйте этот метод для входа пользователя в вашу систему аутентификации Firebase. Сначала получите учетные данные либо непосредственно от пользователя, в случае EmailAuthCredential , либо из поддерживаемого SDK для аутентификации, такого как Google Sign-In или Facebook.
- signInAnonymously () — анонсирует пользователя, не требуя никаких учетных данных. Этот метод создает новую учетную запись в вашей системе аутентификации Firebase, за исключением случая, когда в это приложение уже вошел анонимный пользователь.
- signInWithCustomToken (String) — Пытается войти в систему с помощью пользовательского токена. Используйте этот метод после получения пользовательского токена Firebase Auth с вашего сервера, чтобы войти в систему проверки подлинности Firebase.
Закончив вход в систему, давайте настроим регистрацию.
Измените SignUpActivity
как показано.
package com.echessa.todo; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FirebaseAuth; public class SignUpActivity extends AppCompatActivity { protected EditText passwordEditText; protected EditText emailEditText; protected Button signUpButton; private FirebaseAuth mFirebaseAuth; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_sign_up); // Initialize FirebaseAuth mFirebaseAuth = FirebaseAuth.getInstance(); passwordEditText = (EditText)findViewById(R.id.passwordField); emailEditText = (EditText)findViewById(R.id.emailField); signUpButton = (Button)findViewById(R.id.signupButton); signUpButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick (View v) { String password = passwordEditText.getText().toString(); String email = emailEditText.getText().toString(); password = password.trim(); email = email.trim(); if (password.isEmpty() || email.isEmpty()) { AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity. this ); builder.setMessage(R.string.signup_error_message) .setTitle(R.string.signup_error_title) .setPositiveButton(android.R.string.ok, null ); AlertDialog dialog = builder.create(); dialog.show(); } else { mFirebaseAuth.createUserWithEmailAndPassword(email, password) .addOnCompleteListener(SignUpActivity. this , new OnCompleteListener<AuthResult>() { @Override public void onComplete (@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { Intent intent = new Intent(SignUpActivity. this , MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } else { AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity. this ); builder.setMessage(task.getException().getMessage()) .setTitle(R.string.login_error_title) .setPositiveButton(android.R.string.ok, null ); AlertDialog dialog = builder.create(); dialog.show(); } } }); } } }); } }
Метод createUserWithEmailAndPassword()
пытается создать новую учетную запись пользователя с указанным адресом электронной почты и паролем. В случае успеха он также регистрирует пользователя в приложении. Task
AuthResult
возвращается с результатом операции. Вы проверяете, прошла ли регистрация успешно, и перенаправляете пользователя в MainActivity, в противном случае показываете им сообщение об ошибке. В своем приложении вы можете проверить, не возникло ли исключение, чтобы принять решение об ошибке, которую вы покажете своим пользователям. Ниже приведены возможные исключения, возникающие в случае ошибки при создании учетной записи.
- FirebaseAuthWeakPasswordException выдается, если пароль недостаточно надежен
- FirebaseAuthInvalidCredentialsException выдается, если адрес электронной почты искажен
- Исключение FirebaseAuthUserCollisionException, если учетная запись с указанным адресом электронной почты уже существует
Запустите приложение. Если коснуться текстового представления «Регистрация» в представлении «Вход в систему», откроется представление «Регистрация».
Зарегистрировать аккаунт. По умолчанию firebase выполняет некоторую проверку по умолчанию для данных. Например, ваш пароль должен содержать не менее 6 символов.
Если вы также попытаетесь зарегистрироваться с неверным адресом электронной почты, вы получите следующую ошибку.
Если регистрация прошла успешно, вы будете перенаправлены на MainActivity. Если вы посмотрите на консоль Firebase, вы сможете увидеть созданного пользователя в разделе Auth> Users .
Команда Firebase создала библиотеку с открытым исходным кодом под названием FirebaseUI, которая упрощает процесс добавления аутентификации и подключения общих элементов пользовательского интерфейса к базе данных Firebase. Прочитайте документацию, чтобы узнать больше о библиотеке.
Авторизация и проверка данных
Идентификация вашего пользователя — это только одна часть безопасности. Как только вы узнаете, кто они, вам понадобится способ контролировать их доступ к данным в вашей базе данных Firebase.
Firebase имеет декларативный язык для определения правил, которые существуют на серверах Firebase и определяют безопасность вашего приложения. Вы можете редактировать их на вкладке База данных> Правила .
Правила безопасности позволяют вам контролировать доступ к каждой части вашей базы данных. По умолчанию Firebase имеет правила безопасности, которые требуют, чтобы пользователи проходили аутентификацию.
{ " rules ": { " .read ": "auth != null" , " .write ": "auth != null" } }
Правила базы данных Firebase имеют JavaScript-подобный синтаксис и бывают четырех типов:
- .read — описывает, разрешены ли и когда данные
буду читать пользователям. - .write — описывает, когда и когда данные могут быть записаны.
- .validate — Определяет, как будет выглядеть правильно отформатированное значение, имеет ли оно дочерние атрибуты, и тип данных.
- .indexOn — Определяет дочерний элемент для индексации для поддержки упорядочения и запросов.
.read
и .write
каскадируются, поэтому следующий набор правил предоставляет доступ на чтение для любых данных в пути /foo/
(также может называться узлом foo
), а также для любых более глубоких путей, таких как /foo/bar/baz
. Обратите внимание, что .read
и .write
в базе данных, переопределяют более глубокие правила, поэтому доступ на чтение к /foo/bar/baz
все равно будет предоставлен в этом примере, даже если правило по пути /foo/bar/baz
оценивается как false.
{ " rules ": { " foo ": { " .read ": true , " .write ": false } } }
Правила .validate
не каскадные.
Измените правила, как показано, и нажмите « Опубликовать» .
{ " rules ": { " users ": { " $uid ": { " .read ": "auth != null && auth.uid == $uid" , " .write ": "auth != null && auth.uid == $uid" , " items ": { " $item_id ": { " title ": { " .validate ": "newData.isString() && newData.val().length > 0" } } } } } } }
В приведенных выше правилах auth != null && auth.uid == $uid
ограничивает права на чтение и запись данных на узле users
(а также на его дочерних узлах) для пользователя, чей uid
совпадает с идентификатором вошедшего в систему пользователя ( auth.uid
). $uid
— это переменная, которая содержит значение в этом узле, а не имя самого узла. С этим правилом не только пользователь должен будет проходить аутентификацию для чтения или записи любых данных на узел и его дочерние элементы, но он также будет иметь доступ только к своим собственным данным.
Правила базы данных Firebase включают в себя встроенные переменные и функции, которые позволяют ссылаться на другие пути, временные метки на стороне сервера, информацию об аутентификации и многое другое. Вы можете использовать эти переменные и функции для создания выразительных правил. Эти переменные и функции обеспечивают правилам гибкость и гибкость, позволяя ссылаться на другие пути, временные метки на стороне сервера и т. Д.
В правилах приложений вы используете встроенную переменную auth
. Эта переменная заполняется после аутентификации вашего пользователя. Он содержит данные о пользователе, в том числе auth.uid
, уникальный буквенно-цифровой идентификатор, который работает у разных поставщиков.
Доступные переменные:
- now — Текущее время в миллисекундах со времен Linux. Это особенно хорошо работает для проверки временных отметок, созданных с помощью пакета SDK
firebase.database.ServerValue.TIMESTAMP
. - root — RuleDataSnapshot, представляющий корневой путь в базе данных Firebase в том виде, в каком он существует до предпринятой операции.
- newData — RuleDataSnapshot, представляющий данные, какими они будут существовать после предпринятой операции. Он включает в себя новые записываемые данные и существующие данные.
- data — RuleDataSnapshot, представляющий данные в том виде, в каком они существовали до попытки выполнения операции.
- $ variable — путь подстановочного знака, используемый для представления идентификаторов и динамических дочерних ключей.
- auth — представляет полезную нагрузку токена аутентифицированного пользователя.
Firebase хранит данные в формате JSON. В нашей базе данных каждый пользователь будет иметь массив дел с именами items
. Каждый item
будет иметь title
. Выше вы добавили некоторую проверку данных, которая гарантирует, что данные, записываемые в / users // items, должны быть длиной не более 0 символов. Поэтому item
с пустым title
не будет сохранен.
Убедитесь, что вы нажали кнопку « Опубликовать» , иначе правила не будут сохранены. Вы должны увидеть сообщение «Правила опубликованы».
Правила проверки хороши, но они не должны заменять код проверки данных в вашем приложении. Вы все равно должны проверить ввод в вашем приложении, чтобы улучшить производительность.
Сохранение и получение данных
Добавьте следующие переменные в MainActivity
:
private DatabaseReference mDatabase; private String mUserId;
Затем измените onCreate()
как показано.
@Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Initialize Firebase Auth and Database Reference mFirebaseAuth = FirebaseAuth.getInstance(); mFirebaseUser = mFirebaseAuth.getCurrentUser(); mDatabase = FirebaseDatabase.getInstance().getReference(); if (mFirebaseUser == null ) { // Not logged in, launch the Log In activity loadLogInView(); } else { mUserId = mFirebaseUser.getUid(); // Set up ListView final ListView listView = (ListView) findViewById(R.id.listView); final ArrayAdapter<String> adapter = new ArrayAdapter<>( this , android.R.layout.simple_list_item_1, android.R.id.text1); listView.setAdapter(adapter); // Add items via the Button and EditText at the bottom of the view. final EditText text = (EditText) findViewById(R.id.todoText); final Button button = (Button) findViewById(R.id.addButton); button.setOnClickListener( new View.OnClickListener() { public void onClick (View v) { mDatabase.child( "users" ).child(mUserId).child( "items" ).push().child( "title" ).setValue(text.getText().toString()); text.setText( "" ); } }); // Use Firebase to populate the list. mDatabase.child( "users" ).child(mUserId).child( "items" ).addChildEventListener( new ChildEventListener() { @Override public void onChildAdded (DataSnapshot dataSnapshot, String s) { adapter.add((String) dataSnapshot.child( "title" ).getValue()); } @Override public void onChildChanged (DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved (DataSnapshot dataSnapshot) { adapter.remove((String) dataSnapshot.child( "title" ).getValue()); } @Override public void onChildMoved (DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled (DatabaseError databaseError) { } }); } }
Вы создаете ссылку на корневой узел базы данных с помощью FirebaseDatabase.getInstance().getReference()
. Затем вы устанавливаете прослушиватель на кнопку « Добавить новый элемент» , которая при нажатии сохраняет данные в Firebase.
Существует четыре способа записи данных в базу данных Firebase Realtime:
- setValue () — записывает или заменяет данные по определенному пути, например
users/<user-id>/<username>
. - push () — добавить в список данных. Каждый раз, когда вы вызываете
push()
, Firebase генерирует уникальный ключ, который также может использоваться в качестве уникального идентификатора, напримерuser-posts/<user-id>/<unique-post-id>
. - updateChildren () — обновляет некоторые ключи для определенного пути, не заменяя все данные.
- runTransaction () — обновляет сложные данные, которые могут быть повреждены при одновременном обновлении.
В нашем коде данные сохраняются с:
mDatabase.child( "users" ).child(mUserId).child( "items" ).push().child( "title" ).setValue(text.getText().toString());
.child
получает ссылку на указанный узел, если он существует, или создает его, если он не существует. Выше будет сохранен введенный текст по пути /users/<user id>/items/<item id>/title
. .push()
генерирует новое дочернее местоположение, используя уникальный ключ. Вы используете его для генерации уникального ключа для каждого добавленного item
. .setValue()
записывает или заменяет данные по указанному пути.
Чтобы извлечь данные из Firebase, добавьте прослушиватель к ссылке на базу данных с помощью addChildEventListener()
. Вы можете прослушивать следующие типы событий, которые извлекают данные:
- ValueEventListener : onDataChange () Чтение и прослушивание изменений всего содержимого пути.
- ChildEventListener : onChildAdded () — извлекает списки элементов или прослушивает дополнения к списку элементов. Рекомендуется использовать с
onChildChanged()
иonChildRemoved()
для мониторинга изменений в списках. - ChildEventListener : onChildChanged () — прослушивает изменения элементов в списке. Используйте с
onChildAdded()
иonChildRemoved()
для мониторинга изменений в списках. - ChildEventListener : onChildRemoved () — прослушивает элементы, удаляемые из списка. Используйте с
onChildAdded()
иonChildChanged()
для отслеживания изменений в списках. - ChildEventListener : onChildMoved () — прослушивает изменения порядка элементов в упорядоченном списке.
onChildMoved()
всегда следуют заonChildChanged()
которое вызвало изменение порядка элемента (в зависимости от вашего текущего метода order-by).
Наше простое приложение позволяет только удалять или добавлять элементы в список, поэтому используйте onChildRemoved()
и onChildAdded()
соответственно.
Слушатель получает DataSnapshot
, который является снимком данных. Снимок — это изображение данных в определенном месте в базе данных Firebase в определенный момент времени. Вызов getValue()
для моментального снимка возвращает Java-представление данных. Возможные типы, возвращаемые getValue()
: Boolean
, String
, Long
, Double
, Map<String, Object>
и List<Object>
. Если в этом месте нет данных, снимок вернет значение null
и перед попыткой использования данных рекомендуется проверить наличие null
. Наконец, добавьте полученные данные в представление списка.
Если вы запустите приложение и добавите некоторые элементы, они будут добавлены в список и в базу данных.
Вы можете добавить данные из консоли Firebase, нажав на зеленый + элемент управления на узле и введя данные. Просто убедитесь, что вы вводите данные в правильном формате, иначе приложение Android будет зависать при попытке чтения данных. В производственном приложении вы должны добавить проверку, чтобы убедиться, что она не дает сбой при получении неверных данных.
Чтобы добавить элемент из консоли, щелкните значок + на узле элементов и добавьте следующее в поле имени . Вы должны ввести полный путь // заголовок .
/123/title
Добавьте «Это было добавлено с помощью консоли» в поле значения и нажмите « Сохранить» .
Элемент будет добавлен к данным, и если вы посмотрите на свое приложение для Android, список обновится с помощью элемента.
Текущее приложение отправляет String
на сервер. Это работает, но для более сложного приложения ваши объекты модели будут более сложными.
Firebase позволяет передавать ваш собственный пользовательский Java-объект в DatabaseReference.setValue()
чтения данных в объект с помощью DataSnapshot.getValue()
, при условии, что определяющий его класс имеет конструктор по умолчанию, который не принимает аргументов, и публичные методы получения для свойств, которые должны быть назначены. Подробнее об этом смотрите в документации .
Чтобы увидеть это в действии, создайте класс с именем Item
и измените его на:
package com.echessa.todo; /** * Created by echessa on 8/27/16. */ public class Item { private String title; public Item () {} public Item (String title) { this .title = title; } public String getTitle () { return title; } public void setTitle (String title) { this .title = title; } }
Измените кнопки добавления нового элемента при прослушивании щелчка на:
button.setOnClickListener( new View.OnClickListener() { public void onClick (View v) { Item item = new Item(text.getText().toString()); mDatabase.child( "users" ).child(mUserId).child( "items" ).push().setValue(item); text.setText( "" ); } });
При этом используется объект Item
для сохранения данных в базе данных. Содержимое Item
отображается в дочерние расположения вложенным способом. Запустите приложение, и вы по-прежнему сможете добавлять данные в список и просматривать сохраненные данные на консоли сервера.
Удаление данных
Ваше приложение теперь может сохранять данные и извлекать их для заполнения списка. Затем необходимо разрешить пользователям удалять элементы из списка и Firebase.
Добавьте следующее в onCreate()
блока else
в onCreate()
в классе MainActivity
:
// Delete items when clicked listView.setOnItemClickListener( new AdapterView.OnItemClickListener() { public void onItemClick (AdapterView<?> parent, View view, int position, long id) { mDatabase.child( "users" ).child(mUserId).child( "items" ) .orderByChild( "title" ) .equalTo((String) listView.getItemAtPosition(position)) .addListenerForSingleValueEvent( new ValueEventListener() { @Override public void onDataChange (DataSnapshot dataSnapshot) { if (dataSnapshot.hasChildren()) { DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next(); firstChild.getRef().removeValue(); } } @Override public void onCancelled (DatabaseError databaseError) { } }); } });
Это устанавливает прослушиватель onclick в представлении списка и запрашивает базу данных при нажатии элемента. Он ищет в базе данных Firebase элемент с заголовком, равным строке в повернутом месте. В более сложном приложении вы можете искать что-то уникальное для объекта, например, идентификатор. Затем он удаляет первое вхождение элемента из базы данных. Список автоматически обновляется.
Запустите приложение, и вы сможете удалить элемент из Firebase, нажав на него в приложении для Android.
Выход пользователей
Вызов signout()
делает недействительным токен пользователя и выводит его из вашего приложения. Измените следующие строки в onOptionsItemSelected()
. Это выходит из системы пользователя и перенаправляет его в представление входа в систему.
if (id == R.id.action_logout) { mFirebaseAuth.signOut(); loadLogInView(); }
Вывод
Из этого руководства вы узнали, как использовать Firebase для управления пользовательскими данными приложения Android. Это было вводное руководство, и Firebase предлагает множество не охваченных функций, которые вы можете изучить далее в документации .
Я хотел бы услышать ваши комментарии, идеи и опыт использования Firebase в ваших приложениях .