Статьи

Создание облачного бэкенда для вашего Android-приложения с использованием Firebase

Создание облачного бэкенда для вашего 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 и создайте проект, в котором будут храниться данные вашего приложения.

Create New Project

Введите название и страну / регион для проекта.

Create Firebase Project

Страна / регион представляет страну / регион вашей организации / компании. Ваш выбор также устанавливает соответствующую валюту для отчетов о доходах. После задания имени (я использовал SPToDoApp ) и региона для проекта, нажмите кнопку «Создать проект». Это создаст проект, и откроется его консоль. В консоли проекта нажмите кнопку « Добавить Firebase в приложение для Android» .

Firebase Options

Введите имя пакета вашего проекта Android в появившемся окне. Если ваше приложение будет использовать определенные сервисы Google Play, такие как Google Sign-In, App Invites, Dynamic Links и т. Д., Вам придется предоставить SHA-1 вашего сертификата подписи. Это приложение не будет использовать ни одну из этих служб, поэтому оставьте это поле пустым. Если вы хотите добавить эти сервисы в свое приложение, перейдите на эту страницу для получения информации об использовании keytool, чтобы получить хэш SHA-1 вашего сертификата подписи. На странице содержатся инструкции по получению отпечатков сертификатов выпуска и отладки. Когда закончите, нажмите на кнопку Добавить приложение .

Add Android App to Project

Нажмите « Добавить приложение», чтобы загрузить файл google-services.json на ваш компьютер. На следующей странице диалогового окна приведены инструкции по размещению загруженного файла JSON. Найдите загруженный файл и переместите его в корневой каталог модуля приложения вашего проекта Android.

Add Config File to Android Project

Файл JSON содержит параметры конфигурации, необходимые приложению Android для взаимодействия с серверами Firebase. Он содержит такие детали, как URL-адрес проекта Firebase, ключ API и т. Д. В предыдущей версии Firebase вам приходилось хранить их вручную в коде приложения, но теперь этот процесс был упрощен благодаря использованию одного файла, который содержит необходимые данные.

Если вы используете контроль версий и храните свой код в общедоступном репозитории, вам следует рассмотреть возможность размещения файла google-services.json в файле .gitignore , чтобы эта информация не была общедоступной.

Когда закончите, нажмите кнопку « Продолжить» в диалоговом окне, и вы получите дальнейшие инструкции по настройке.

Add Libraries

В свой файл build.gradle уровня проекта добавьте следующее в узел buildscript> зависимости . Это сделано для включения подключаемого модуля служб Google для Gradle, который загружает загруженный файл google-services.json .

classpath 'com.google.gms:google-services:3.0.0' 

Убедитесь, что вы редактируете правильный файл Gradle.

Project Level Gradle File

Затем в файле build.gradle уровня приложения добавьте следующее в нижней части файла, чтобы включить плагин Gradle.

 apply plugin: 'com.google.gms.google-services' 

Опять же, убедитесь, что это правильный файл build.gradle .

App-level Gradle File

Затем добавьте следующие зависимости в тот же файл. 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 и откройте панель управления вашего проекта, нажав на проект.

Firebase Project

Если вы выберете « База данных» на левой панели, вы сможете увидеть данные проекта в формате JSON.

Firebase хранит все данные базы данных в виде объектов JSON. Там нет таблиц или записей. Когда вы добавляете данные в дерево JSON, оно становится ключом в существующей структуре JSON.

На данный момент вы можете видеть только корневой узел.

No Data

Если вы наведите курсор мыши на узел, вы увидите элементы управления + и x , которые можно использовать для добавления данных в дерево и удаления этого узла соответственно.

На левой панели нажмите « Auth» и затем выберите вкладку « Sign In Method » справа. Включить аутентификацию по электронной почте / паролю от указанных провайдеров.

Password and Email Authentication

В 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() который перенаправляет пользователя в представление входа в систему.

Запустите приложение, и вы будете перенаправлены на страницу входа.

Login

Добавьте следующее в 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, в противном случае выводите им сообщение об ошибке. Ниже приведено сообщение об ошибке, полученное при попытке войти в систему пользователя, который не зарегистрирован.

Firebase Authentication Error

Возможно, вы захотите показать пользователю лучшее сообщение, чем возвращаемое task.getException().getMessage() в вашем приложении. Вы можете проверить наличие исключения, чтобы определить сообщение об ошибке, чтобы показать пользователю. Вы можете получить одно из следующих исключений при сбое аутентификации:

Помимо 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, в противном случае показываете им сообщение об ошибке. В своем приложении вы можете проверить, не возникло ли исключение, чтобы принять решение об ошибке, которую вы покажете своим пользователям. Ниже приведены возможные исключения, возникающие в случае ошибки при создании учетной записи.

Запустите приложение. Если коснуться текстового представления «Регистрация» в представлении «Вход в систему», откроется представление «Регистрация».

Signup

Зарегистрировать аккаунт. По умолчанию firebase выполняет некоторую проверку по умолчанию для данных. Например, ваш пароль должен содержать не менее 6 символов.

Firebase Signup Error - Short Password

Если вы также попытаетесь зарегистрироваться с неверным адресом электронной почты, вы получите следующую ошибку.

Firebase Signup Error - Invalid Email Format

Если регистрация прошла успешно, вы будете перенаправлены на MainActivity. Если вы посмотрите на консоль Firebase, вы сможете увидеть созданного пользователя в разделе Auth> Users .

Firebase User

Команда 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 .
  • rootRuleDataSnapshot, представляющий корневой путь в базе данных Firebase в том виде, в каком он существует до предпринятой операции.
  • newDataRuleDataSnapshot, представляющий данные, какими они будут существовать после предпринятой операции. Он включает в себя новые записываемые данные и существующие данные.
  • dataRuleDataSnapshot, представляющий данные в том виде, в каком они существовали до попытки выполнения операции.
  • $ 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 . Наконец, добавьте полученные данные в представление списка.

Если вы запустите приложение и добавите некоторые элементы, они будут добавлены в список и в базу данных.

Added Items on List View

Added Items on Firebase Console

Вы можете добавить данные из консоли Firebase, нажав на зеленый + элемент управления на узле и введя данные. Просто убедитесь, что вы вводите данные в правильном формате, иначе приложение Android будет зависать при попытке чтения данных. В производственном приложении вы должны добавить проверку, чтобы убедиться, что она не дает сбой при получении неверных данных.

Чтобы добавить элемент из консоли, щелкните значок + на узле элементов и добавьте следующее в поле имени . Вы должны ввести полный путь // заголовок .

 /123/title 

Добавьте «Это было добавлено с помощью консоли» в поле значения и нажмите « Сохранить» .

Элемент будет добавлен к данным, и если вы посмотрите на свое приложение для Android, список обновится с помощью элемента.

Updated Items on List View

Updated Items on Dashboard

Текущее приложение отправляет 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 в ваших приложениях .