Làm việc với Firebase Realtime Database
Firebase Realtime database là một cloud hosted database hỗ trợ đa nền tảng: Android, IOS và Web. Tất cả dữ liệu được lưu trữ ở định dạng JSON và với bất kể một sự thay đổi dữ liệu nào thì có sự phản hồi ngay lập tức, hiển thị đồng bồ trên các nền tảng và các thiết bị. Bài hướng dân này xây dựng ...
Firebase Realtime database là một cloud hosted database hỗ trợ đa nền tảng: Android, IOS và Web. Tất cả dữ liệu được lưu trữ ở định dạng JSON và với bất kể một sự thay đổi dữ liệu nào thì có sự phản hồi ngay lập tức, hiển thị đồng bồ trên các nền tảng và các thiết bị. Bài hướng dân này xây dựng nhằm thể hiện sự phản hồi theo thời gian tực của apps một cách đơn giản khi sử dụng Firebase Realtime database.
Phần 1. Giới thiệu những đặc điểm nổi bật của Firebase Realtime database
1) Dữ liệu được lưu trữ như nào - Định dạng Json
Firebase realtime database lưu trữ dữ liệu theo định dạng JSON. Về cơ bản thì toàn bộ dữ liệu là một JSON tree lớn cùng với nhiều điểm node. Nên khi bạn xây dựng dữ liệu, bạn cần chuẩn bị một cấu trúc json để dễ dàng cho việc truy cập tránh việc các node con bị lồng nhau
Dưới đây là một ví dụ về việc lưu trữ danh sách các hồ sơ người dùng và các bài viết trong cây json. Bạn có thể tìm hiểu về cấu trúc dữ liệu tại đây để thực hành
{ "users": [ { "name": "Ravi Tamada", "email": "ravi@androidhive.info", "address": "XXX, XXXX, 1234" } ], "posts": [ { "id": 100, "author": "Ravi Tamada", "content": "This is awesome firebase realtime database...", "timestamp": "13892733894" } ] }
2) Dữ liệu offline
Firebase cung cấp sự hỗ trợ tuyệt vời khi nói đến dữ liệu offline. Nó tự động lưu trữ offline khi không có kết nối internet. Tuy nhiên nó sẽ cho phép lưu trữ vào ổ đĩa persistence khi data offline thậm chí khi ứng dụng restart. Ổ đĩa persistence có thể gọi đến bởi dòng code ở phía dưới. Xem hướng dẫn data offline tại đây
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
3) Thực hiện các thao tác CRUD
Trước khi thực hành. Tôi sẽ hướng dẫn các bạn những thông tin cơ bản về việc thực thi các phương thức CRUD
Để thực hiện bất kỳ phương thức nào trên cơ sở dữ liệu cho dù đó có thể được đọc hoặc ghi, bạn cần phải có được các tham chiếu đến cơ sở dữ liệu. Các mã dưới đây cho phép bạn tham chiếu đến nút trên cùng của cơ sở dữ liệu JSON. Từ đây bạn cần phải sử dụng các tên nút con phải đi qua các nút khác
private DatabaseReference mDatabase; mDatabase = FirebaseDatabase.getInstance().getReference();
3.1. Inserting Data
Để thêm dữ liệu, bạn có thể sử dụng phương thức setValue() trên đường dẫn tham chiếu đến database. Nó sẽ tạo mới và cập nhật giá trị trên đường dẫn được cung cấp. Ví dụ ở dưới đã mã để thêm 1 nút được gọi là “copyright” trên cây json ở mức đỉnh
DatabaseReference mRef = mDatabase.getReference("copyright"); mRef.setValue("©2016 androidhive. All rights Reserved");
Realtime database chấp nhận nhiều loại dữ liệu: String, Long, Double, Boolean, Map<String, Object>, List<Object> để lưu trữ dữ liệu. Bạn cũng có thể sử dụng dữ liệu tùy biến của đối tượng để lưu trữ dữ liệu, điều này rất hữu dụng khi lưu trữ đối tượng vào database một cách trực tiếp
Giả sử bạn muốn lưu trữ thông tin người dùng vào database. Đầu tiên bạn cần tạo User model cùng với constructor rỗng and những thuộc tính khác
@IgnoreExtraProperties public class User { public String name; public String email; // Default constructor required for calls to // DataSnapshot.getValue(User.class) public User() { } public User(String name, String email) { this.name = name; this.email = email; } }
Mọi người dùng cần một ID duy nhất, bạn có thể tạo ra một bằng cách gọi phương thức push () để tạo ra một nút trống rỗng, với khóa duy nhất. Sau đó, được tham chiếu đến nút 'user' bằng phương thức child(). Cuối cùng sử dụng phương thức setValue() để lưu trữ dữ liệu người dùng.
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("users"); // Creating new user node, which returns the unique key value // new user node would be /users/$userid/ String userId = mDatabase.push().getKey(); // creating user object User user = new User("Ravi Tamada", "ravi@androidhive.info"); // pushing user to 'users' node using the userId mDatabase.child(userId).setValue(user);
Bằng việc chạy đoạn code ở dưới, 1 nút người dùng mới sẽ được chèn vào database cùng với giá trị khóa duy nhất. Nhìn chung, user id nên được lấy lại bằng cách thực hiện xác thực Firebase trong ứng dụng mà bạn cung cấp AUTHID hoạt động như người sử dụng id.
{ "users": [ "-KTYWvZG4Qn9ZYTc47O6" : { "email" : "ravi@androidhive.info", "name" : "Ravi Tamada" }, { ... } ] }
3.2. Reading Data
Để đọc dữ liệu, bạn cần đính kèm phương thức ValueEventListener() để tham chiếu database. Sự kiện này sẽ được kích hoạt bất cứ khi nào có sự thay đổi trong dữ liệu trong thời gian thực. Trong onDataChange(), bạn có thể thực hiện các phương thức mong muốn vào dữ liệu mới.
Dưới đây là sự kiện lắng nghe được kích hoạt bất cứ khi nào có sự thay đổi trong dữ liệu hồ sơ người dùng mà đã tạo ra trước đó.
mDatabase.child(userId).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { User user = dataSnapshot.getValue(User.class); Log.d(TAG, "User name: " + user.getName() + ", email " + user.getEmail()); } @Override public void onCancelled(DatabaseError error) { // Failed to read value Log.w(TAG, "Failed to read value.", error.toException()); } });
3.3. Updating Data
Để cập nhật dữ liệu, bạn có thể sử dụng cùng phương pháp setValue() để passing giá trị mới. Bạn cũng có thể sử dụng phương thức updateChildren() để passing đường dẫn để cập nhật dữ liệu mà không làm ảnh hưởng đến các nút con khác.
Ví dụ, nếu bạn muốn cập nhật chỉ email của người dùng, bạn có thể sử dụng đoạn code bên
String newEmail = 'androidhive@gmail.com'; mDatabase.child(userId).child("email").setValue(newEmail);
3.4. Inserting Data
Để xóa dữ liệu, bạn có thể gọi phương thức removeValue() trong tham chiếu database. Bạn cũng có thể pass qua null để gọi phương thức setValue(), nó giống như phương thức xóa
Bạn có thể tìm hiểu nhiều hơn về CRUD thông qua tài liệu nâng cao ở tại đây
4) Tính bảo mật và quy định
Quy định của Firebase cung cấp một cách để xác định vai trò người dùng khi thực hiện đọc và ghi. Những quy định này sẽ đóng vai trò một lớp bảo mật trên máy chủ trước khi thực hiện bất kỳ hoạt động CRUD. Mặc định các quy định cho phép người dùng thực hiện các hoạt động đọc và ghi chỉ sau khi xác thực.
Các quy tắc dưới đây cho phép người sử dụng chứng thực chỉ để đọc hoặc ghi dữ liệu.
{ "rules": { ".read": "auth != null", ".write": "auth != null" } }
Dưới đây quy tắc cho phép tất cả mọi người để đọc và ghi dữ liệu mà không cần xác thực.
{ "rules": { ".read": true, ".write": true } }
Bạn cũng có thể sử dụng các quy tắc để xác nhận dữ liệu trước khi chèn vào cơ sở dữ liệu. Ví dụ dưới đây quy tắc xác nhận tên được ít hơn 50 ký tự và gửi email có giá trị sử dụng email thường xuyên.
{ "rules": { ".read": true, ".write": true, "users": { "$user": { "name": { ".validate": "newData.isString() && newData.val().length < 50" }, "email": { ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i)" } } } } }
Go through firebase security & rules guide to learn more about the security concepts.
Tìm hiểu về tính bảo mật và quy định của Firebase tại đây để tìm hiểu thêm
Phần 2. Demo ứng dụng sử dụng Firebase Realtime database
Bước 1
Điều đầu tiên bạn cần làm là đi đến https://firebase.google.com/ và tạo một tài khoản để truy cập vào giao diện điều khiển. Sau khi bạn truy cập vào giao diện điều khiển, bạn có thể bắt đầu bằng cách tạo ra các dự án đầu tiên của bạn.
Bước 2
Cung cấp tên package của project và mã SHA-1. Sau đó file google-services.json sẽ được tự động được tải về sau khi ấn vào nút add app
Bước 3
Tạo một dự án mới trong Android Studio từ File ⇒ Dự án mới. Trong khi điền các thông tin chi tiết dự án, sử dụng cùng một tên gói mà bạn đã đưa vào giao diện của Firebase
Bước 4
Paste file google-services.json vào thư mục app của dự án của bạn. Bước này rất quan trọng như dự án của bạn sẽ không được xây dựng mà không có tập tin này.
Bước 5
Bây giờ mở build.gradle nằm trong thư mục chính của dự án và thêm đoạn mã google play
build.gradle dependencies { classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }
Bước 6
Mở app/build.gradle sau đó thêm firebase database dependency. Ở cuối file, thêm apply plugin: ‘com.google.gms.google-services’
app/build.gradle dependencies { // Adding support library for this demo app compile 'com.android.support:design:24.2.1' compile 'com.google.firebase:firebase-database:9.6.1' } apply plugin: 'com.google.gms.google-services'
Bước 7
Để lưu trữ hồ sơ người dùng, chúng ta cần một lớp model được gọi User.java. Tạo một lớp có tên User.java và thêm vào dưới đây thuộc tính của lớp. Nếu bạn muốn bạn có thể thêm vài chi tiết tài sản như địa chỉ, điện thoại di động, vv,
User.java package info.androidhive.firebase; import com.google.firebase.database.IgnoreExtraProperties; /** * Created by Ravi Tamada on 07/10/16. * www.androidhive.info */ @IgnoreExtraProperties public class User { public String name; public String email; // Default constructor required for calls to // DataSnapshot.getValue(User.class) public User() { } public User(String name, String email) { this.name = name; this.email = email; } }
Bước 8
Mở file layout của activity_main.xml. Tạo ra layout đơn giản, để bạn có thể nhập dữ liệu hồ sơ để lưu trữ trong cơ sở dữ liệu.
activity_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" android:id="@+id/activity_main" android:layout_awidth="match_parent" android:layout_height="match_parent" android:orientation="vertical" 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="info.androidhive.firebase.MainActivity"> <TextView android:id="@+id/txt_user" android:layout_awidth="wrap_content" android:layout_height="wrap_content" android:paddingBottom="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_horizontal_margin" android:textSize="20dp" /> <LinearLayout android:layout_awidth="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.design.widget.TextInputLayout android:layout_awidth="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/name" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:hint="@string/name" android:inputType="textCapWords" android:maxLines="1" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_awidth="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/email" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:hint="@string/email" android:inputType="textEmailAddress" android:maxLines="1" /> </android.support.design.widget.TextInputLayout> <Button android:id="@+id/btn_save" style="?android:textAppearanceSmall" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:background="@color/colorPrimary" android:text="@string/action_save" android:textColor="@android:color/white" android:textStyle="bold" /> </LinearLayout> </LinearLayout>
Bước 9
Mở MainActivity.java và thêm đoạn mã ở dưới cần thiết. Mã này là rất đơn giản và dễ hiểu.
Mục tiêu của chúng tôi là tạo ra các cấu trúc JSON như dưới đây, trong đó các 'APP_TITLE' lưu trữ tên ứng dụng.'users' lưu trữ hồ sơ người dùng như là một mảng của các nút.
{ "app_title" : "Realtime Database", "users" : { "-KTYWvZG4Qn9ZYTc47O6" : { "email" : "ravi@androidhive.info", "name" : "Ravi Tamada" } } }
GetReference("APP_TITLE") tạo ra một nút tên APP_TITLE mà các cửa hàng tiêu đề trên thanh công cụ.
GetReference("users") được tham chiếu đến nút người sử dụng.
CreateUser() phương pháp cửa hàng một người dùng mới trong cơ sở dữ liệu thời gian thực
UpdateUser() cập nhật phương pháp sử dụng các thông tin như tên và email.
MainActivity.java package info.androidhive.firebase; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.ValueEventListener; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private TextView txtDetails; private EditText inputName, inputEmail; private Button btnSave; private DatabaseReference mFirebaseDatabase; private FirebaseDatabase mFirebaseInstance; private String userId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Displaying toolbar icon getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setIcon(R.mipmap.ic_launcher); txtDetails = (TextView) findViewById(R.id.txt_user); inputName = (EditText) findViewById(R.id.name); inputEmail = (EditText) findViewById(R.id.email); btnSave = (Button) findViewById(R.id.btn_save); mFirebaseInstance = FirebaseDatabase.getInstance(); // get reference to 'users' node mFirebaseDatabase = mFirebaseInstance.getReference("users"); // store app title to 'app_title' node mFirebaseInstance.getReference("app_title").setValue("Realtime Database"); // app_title change listener mFirebaseInstance.getReference("app_title").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Log.e(TAG, "App title updated"); String appTitle = dataSnapshot.getValue(String.class); // update toolbar title getSupportActionBar().setTitle(appTitle); } @Override public void onCancelled(DatabaseError error) { // Failed to read value Log.e(TAG, "Failed to read app title value.", error.toException()); } }); // Save / update the user btnSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String name = inputName.getText().toString(); String email = inputEmail.getText().toString(); // Check for already existed userId if (TextUtils.isEmpty(userId)) { createUser(name, email); } else { updateUser(name, email); } } }); toggleButton(); } // Changing button text private void toggleButton() { if (TextUtils.isEmpty(userId)) { btnSave.setText("Save"); } else { btnSave.setText("Update"); } } /** * Creating new user node under 'users' */ private void createUser(String name, String email) { // TODO // In real apps this userId should be fetched // by implementing firebase auth if (TextUtils.isEmpty(userId)) { userId = mFirebaseDatabase.push().getKey(); } User user = new User(name, email); mFirebaseDatabase.child(userId).setValue(user); addUserChangeListener(); } /** * User data change listener */ private void addUserChangeListener() { // User data change listener mFirebaseDatabase.child(userId).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { User user = dataSnapshot.getValue(User.class);