[Android] Tích hợp Firebase Realtime Database vào ứng dụng Android

Đăng bởi: Admin | Lượt xem: 4223 | Chuyên mục: Android

Firebase Realtime Database là một Cloud Hosted Database hỗ trợ đa nền tảng: Android, IOS và Web. Bất kể một sự thay đổi dữ liệu nào đều có sự phản hồi ngay lập tức và đồng bộ trên các nền tảng và thiết bị. Cùng tìm hiểu cách tích hợp Firebase Realtime Database vào ứng dụng Android trong bài viết này nhé!


Những đặc điểm nổi bật của Firebase Realtime Database

1. Tất cả dữ liệu được lưu trữ ở đị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

Bạn có thể tìm hiểu về cấu trúc dữ liệu tại đây

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

{
  "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

Để 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. Đoạn code 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

private DatabaseReference mDatabase;

mDatabase = FirebaseDatabase.getInstance().getReference();

3.1. Thêm (Create)

Để 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 đã 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 và 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 rỗng với khóa duy nhất. Sau đó, 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ư user id.

{
  "users": [
    "-KTYWvZG4Qn9ZYTc47O6" : {
      "email" : "ravi@androidhive.info",
      "name" : "Ravi Tamada"
    },
    {
      ...
    }
  ]
}

3.2. Đọc (Read)

Để đọc dữ liệu, bạn cần thêm 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 khi có 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à đã được 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. Cập nhật (Update)

Để cập nhật dữ liệu, bạn có thể sử dụng cùng phương thức 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. Xóa (Delete)

Để 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.

Ví dụ 1: Chỉ cho phép đọc hoặc ghi dữ liệu sau khi xác thực

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Ví dụ 2: Cho phép đọ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ụ 3: Quy tắc xác nhận tên được chỉ ít hơn 50 ký tự và email đúng

{
    "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)"
                }
            }
        }
    }
}

Tìm hiểu về tính bảo mật và quy định của Firebase tại đây

Thực hành sử dụng Firebase Realtime Database

Mục tiêu:

Tạo ra cấu trúc JSON như dưới đây:

{
  "app_title" : "Realtime Database",
  "users" : {
    "-KTYWvZG4Qn9ZYTc47O6" : {
      "email" : "ravi@androidhive.info",
      "name" : "Ravi Tamada"
    }
  }
}

Trong đó:

  • "app_title" là tên ứng dụng
  • "users" lưu các user dưới dạng mảng

Bước 1:

Đầu tiên, truy cập 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 và tạo ra các dự án của bạn.

Bước 2: 

Thêm package name 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: 

Thêm file google-services.json vào thư mục app trong dự án của bạn.

Bước 4: 

Thêm các phụ thuộc vào file build.gradle (project)

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 5: 

Thêm các phụ thuộc vào file build.gradle (Module: app)

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 6: 

Tạo User model

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 7:

Thiết kế giao diện

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_width="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_width="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_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/name"
                android:layout_width="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_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/email"
                android:layout_width="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_width="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 8:

Thêm code vào MainActivity.java

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);

                // Check for null
                if (user == null) {
                    Log.e(TAG, "User data is null!");
                    return;
                }

                Log.e(TAG, "User data is changed!" + user.name + ", " + user.email);

                // Display newly updated name and email
                txtDetails.setText(user.name + ", " + user.email);

                // clear edit text
                inputEmail.setText("");
                inputName.setText("");

                toggleButton();
            }

            @Override
            public void onCancelled(DatabaseError error) {
                // Failed to read value
                Log.e(TAG, "Failed to read user", error.toException());
            }
        });
    }

    private void updateUser(String name, String email) {
        // updating the user via child nodes
        if (!TextUtils.isEmpty(name))
            mFirebaseDatabase.child(userId).child("name").setValue(name);

        if (!TextUtils.isEmpty(email))
            mFirebaseDatabase.child(userId).child("email").setValue(email);
    }
}

Kết quả: 

Hi vọng sau bài viết này các bạn đã biết cách cài đặt và sử dụng Firebase Realtime Database và có thể áp dụng vào các dự án sau này.

Cảm ơn các bạn đã đọc bài viết.

Chào thân ái và quyết thắng!

vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!



Khóa học liên quan

Khóa học: Android

Học Kotlin cơ bản
Số bài học:
Lượt xem: 17611
Đăng bởi: Admin
Chuyên mục: Android

Học lập trình Flutter cơ bản
Số bài học:
Lượt xem: 58507
Đăng bởi: Admin
Chuyên mục: Android

Lập trình Android cơ bản
Số bài học:
Lượt xem: 22990
Đăng bởi: Admin
Chuyên mục: Android