Tư Vấn Blockchain
Tạo Cloud Backend Cho Ứng Dụng Android trong Firebase
Với Firebase, bạn có thể lưu trữ và đồng bộ hóa dữ liệu lên NoSQL cloud database. Dữ liệu được lưu trữ ở dạng JSON, được đồng bộ hóa với tất cả client được kết nối theo thời gian thật, và vẫn dùng được khi ứng dụng offline.

Firebase cung cấp các API cho phép bạn xác minh người dùng thông qua email và password, Facebook, Twitter, GitHub, Google, xác minh ẩn danh, hoặc liên kết với các hệ thống xác minh đã có. Bên cạnh khả năng xác minh cùng Realtime Database, Firebase còn “ăn nằm với” nhiều dịch vụ khác như Cloud Messaging, Storage, Hosting, Remote Config, Test Lab, Crash Reporting, Notification, App Indexing, Dynamic Links, Invites, AdWords, AdMob.
Bài viết này sẽ hướng dẫn các bạn cách tạo lập một ứng dụng đơn giản, giúp bạn làm quen với phương pháp lưu-nhận dữ liệu từ Firebase, cũng như cách xác minh người dùng, trao quyền đọc/viết dữ liệu và phê duyệt dữ liệu trên server.
Bạn có thể tìm đoạn code của project này trên GitHub.
Setting up the Project
Để bắt đầu, bạn hãy tạo project tên “To Do”. Ở cửa sổ tiếp theo, set Minimum SDK thành API 15, và lựa chọn Basic Activity ở cửa sổ kế tiếp nữa. Nhấp Finish tại cửa sổ cuối cùng, để tất cả setting như mặc định.
Trước khi khởi động dự án Android, hãy truy cập vào firebase.google.com và tạo tài khoản. Sau khi đăng nhập vào tài khoản, tìm đến Firebase console và tạo project mới (project này sẽ lưu trữ tất cả dữ liệu của bạn).
Nhập tên quốc gia/khu vực của project.

Phần country/region đại diện cho quốc gia/khu vực của công ty/tổ chức bạn đang làm việc. Lựa chọn của bạn tại đây cũng sẽ thiết lập giá trị tiền tệ thích hợp để sử dụng trong các bản báo cáo doanh thu. Sau khi đặt tên (Tôi dùng SPToDoApp) và khu vực cho project, nhấp vào Create Project. Như vậy, bạn đã tạo được project, và console của project sẽ mở lên. Từ console của project, nhấn vào tùy chọn Add Firebase to your Android App.

Nhập tên package của Android project vào cửa sổ pop up. Nếu ứng dụng của bạn dự định dùng các dịch vụ Google như Google Sign-In, App Invites, Dynamic Links,… thì bạn sẽ phải cung vấp SHA-1 của signing certificate. Vì ứng dụng trong bài viết không dùng đến các dịch vụ này, nên ta sẽ để trống. Khi bạn muốn thêm những dịch vụ này vào ứng dụng, truy cập vào đây lấy thông tin bằng keytool để có SHA1-1 hash bạn cần.

Nhấp vào Add App, một file google-services.json sẽ được tải xuống máy tính. trang tiếp theo của hộp hội thoại sẽ hiển thị các chỉ dẫn thay thế file JSON đã tải xuống. Định vị file và chuyển file này vào thư mục root mô-đun ứng dụng của project Android.

File JSON chứa các tùy chọn cài đặt cần thiết để giao tiếp với server Firebase, với các thông tin như URL đến Firebase project, API key,… Trong phiên bản Firebase trước, bạn phải lưu trữ thủ công những thông tin này trong code của ứng dụng. Nhưng giờ đây, quy trình này đã được đơn giản hóa bằng một file thông tin duy nhất.
Nếu đang dùng version control và lưu trữ code trong public repo, bạn nên xem xét đặt file google-services.json ngay trong file .gitignore, để tránh public thông tin này.
Khi đã hoàn thành, tiếp tục nhấp Continue trên cửa sổ hội thoại và chuyển sang các bước tiếp theo.

Vào file build.gradle cấp project, thêm đoạn sau vào node buildscript > dependencies. Bước này nhằm thêm plugin dịch vụ Google cho Gradle, và load file google-services.json bạn tải trước đó.
| 1 2 3 | classpath   'com.google.gms:google-services:3.0.0' | 
Hãy kiểm tra kỹ bạn đã edit đúng file gradle chưa.

Sau đó, trong file build.gradle cấp ứng dụng, thêm đoạn sau vào cuối file để kích hoạt Gradle plugin.
| 1 2 3 | apply  plugin :   'com.google.gms.google-services' | 
Một lần nữa, kiểm tra xem có đúng file build.gradle hay không.

Sau đó, thêm các dependency sau vào cùng file đó. Firebase dùng các SDK khác nhau cho từng tính năng khác nhau. Đến đây, bạn hãy thêm thư viện cần có để dùng được Realtime Database và một thư viện nữa để Xác minh.
| 1 2 3 4 | compile   'com.google.firebase:firebase-database:9.4.0' compile   'com.google.firebase:firebase-auth:9.4.0' | 
Chọn menu File -> New -> Activity -> Empty Activity để tạo activity trống, và đặt tên là LogInActivity. Tạo một activity với tên SignUpActivity.
Thay đổi nội dung trong file layout activity_log_in.xml thành:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <   ? 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 > < / 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" > < / edittext >      < 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" > < / button >      < 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" > < / textview > < / relativelayout > | 
Thay đổi nội dung trong file layout activity_sign_up.xml thành:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <   ? 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 > < / 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" > < / edittext >      < 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" > < / button > < / relativelayout > | 
Những layout này sẽ dùng để theo dõi Login và Signup.
Thêm đoạn dưới đây vào values/strings.xml. 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | < 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 > | 
Trong activity_main.xml, xóa bỏ markup FloatingActionButton. 
| 1 2 3 4 5 6 7 8 9 | < 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" > < / android > | 
Và trong MainActivity.java, hãy xóa luôn đoạn code FAB.
| 1 2 3 4 5 6 7 8 9 10 | 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 ( ) ;      } } ) ; | 
Trong res/menu/menu_main.xml, thay thế settings item bằng Logout item sau: 
| 1 2 3 4 5 6 | < item  android : id = "@+id/action_logout"      android : orderInCategory = "100"      android : title = "@string/action_logout"      app : showAsAction = "never" > < / item > | 
Mở MainActivity và thay thế id action_settings với id action_logout trong onOptionsItemSelected(MenuItem). 
| 1 2 3 4 5 | if   ( id   ==   R . id . action_logout )   {      return   true ; } | 
Menu item này sẽ đăng thoát người dùng.
Thay đổi content_main.xml thành:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <   ? 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 > < 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" > < / edittext >          < button  android : id = "@+id/addButton"              android : layout_width = "match_parent"              android : layout_height = "wrap_content"              android : text = "@string/add_item" > < / button >      < / linearlayout > | 
activity_main.xml có tag include, chỉ đến và load content_main.xml. Ứng dụng sẽ hiển thị các To Do item trong một list view (giao diện danh sách) chiếm dụng phần lớn màn hình. Ở cuối màn hình, bạn sẽ thấy một trường edit text và nút nhấn để thêm item vào danh sách.
Khi đã thiết đặt xong UI của ứng dụng, bạn đến đây sẽ phải tìm cách lưu trữ và truy xuất dữ liệu đến/từ Firebase.
Bảo mật và Quy luật
Trước khi có thể truy xuất và lưu trữ dữ liệu từ server Firebase, bạn cần phải thiết đặt các bước xác minh và thêm quy luật để giới hạn truy cập đến dữ liệu và phê duyệt input của người dùng trước khi lưu.
Authentication (Xác minh)
API của Firebase có các phương thức xác minh built-in như sau: email/password, Facebook, Twitter, GitHub, Google and xác minh ẩn danh. Trong bài viết, ta sẽ xét đến xác minh email và password.
Vào Firebase console, nhấn vào project để mở Dashboard của project.

Nếu lựa chọn Database từ panel bên trái, bạn có thể thấy được dữ liệu của project theo định dạnh JSON.
Firebase lưu trữ tất cả dữ liệu database dưới dạng JSON object, nên sẽ không có table hay record. Khi thêm dữ liệu vào cây JSON, nó sẽ trở thành một key trong cấu trúc JSON đã có.
Lúc này, bạn chỉ thấy được node root.

Nếu bạn rơ chuột trên một node, bạn sẽ thấy các điều khiển + và x mà bạn có thể sử dụng để thêm dữ liệu vào cây hay để chỉ xóa riêng node đó.
Từ panel bên trái, nhấp vào Auth và sau đó lựa chọn tab Sign In Method phía bên phải. Kích hoạt xác minh Email/Password từ các nhà cung cấp được hỗ trợ.

Trong Android Studio, thêm method sau vào MainActivity. 
| 1 2 3 4 5 6 7 8 | 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 ) ; } | 
Đoạn lệnh này sẽ điều hướng đến giao diện Login và làm sạch activity stack. Đồng thời ngăn người dùng quay lại activity chính khi họ nhấn Back button từ giao diện login.
Thêm hai biến sau vào class: 
| 1 2 3 4 | private   FirebaseAuth  mFirebaseAuth ; private   FirebaseUser  mFirebaseUser ; | 
Sau đó, thêm đoạn code sau vào cuối onCreate(). 
| 1 2 3 4 5 6 7 8 9 10 | // Initialize Firebase Auth mFirebaseAuth   =   FirebaseAuth . getInstance ( ) ; mFirebaseUser   =   mFirebaseAuth . getCurrentUser ( ) ; if   ( mFirebaseUser   ==   null )   {      // Not logged in, launch the Log In activity      loadLogInView ( ) ; } | 
Tại đây, bạn sẽ kiểm tra được các user đã đăng nhập. Nếu người dùng đăng nhập được, kết quả sẽ là một object FirebaseUser chứa các chi tiết về người dùng đó. Nếu người dùng không đăng nhập được, getCurrentUser() sẽ trả kết quả null, đồng thời loadLogInView() được call ra để điều hướng người dùng trở lại giao diện đăng nhập.
Chạy ứng dụng và bạn sẽ được điều hướng sang trang đăng nhập.

Thêm đoạn sau vào LogInActivity. 
| 1 2 3 4 5 6 7 | protected   EditText  emailEditText ; protected   EditText  passwordEditText ; protected   Button  logInButton ; protected   TextView  signUpTextView ; private   FirebaseAuth  mFirebaseAuth ; | 
Thay đổi method onCreate() của class LogInActivity sang: 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | @ 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 > < 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 ( ) ;                                  }                              }                          } ) ;              }          }      } ) ; } | 
Thay đổi trên sẽ khởi động view elements (thành tố giao diện) và object FirebaseAuth, entry point của Firebase Authentication SDK. Một event listener sẽ thêm vào Sign Up text view, và mở Sign Up activity khi được nhấp vào. Một event listener nữa trên nút Login sẽ thực hiện phê duyệt input của người dùng, để đảm bảo người dùng đã nhập text cho cả hai trường. Event listener sau đó sẽ tiếp tục call Firebase server với signInWithEmailAndPassword(). Hàm này tiếp nhận email và password của người dùng và trả kết quả Task của AuthResult. Bạn sẽ kiểm tra xem liệu Task có thành công hay không và điều hướng người dùng về MainActivity, nếu không thành công thì error message sẽ hiển thị. Dưới đây là error message xuất hiện khi đăng nhập user chưa đăng ký.

Bạn hoàn toàn có thể thay đổi message do task.getException().getMessage() hiển thị. Bạn có thể sử dụng các kiểu exception sau nếu quá trình xác minh thất bại:
- FirebaseAuthInvalidUserException được thrown nếu tài khoản người dùng có email không tồn tại hoặc đã bị vô hiệu hóa.
- FirebaseAuthInvalidCredentialsException được thrown nếu sai password.
Bênh cạnh signInWithEmailAndPassword(), các bạn có thể đăng nhập người dùng bằng các cách thức sau:
- signInWithCredential(AuthCredential)– Đăng nhập người dùng với AuthCredential. Sử dụng method này để đăng nhập người dùng vào hệ thống Xác minh của Firebase. Trước hết, thu nhận credential (thông tin xác nhận) trực tiếp từ người dùng nếu dùng EmailAuthCredential, hoặc từ các SDK xác minh được hỗ trợ, như Google Sign-In hay Facebook.
- signInAnonymously() – Đăng nhập người dùng ẩn danh mà không yêu cầu bất cứ credential nào. Method này sẽ tạo tài khoản mới trong hệ thống Xác minh Firebase của bạn, trừ trường hợp đã có người dùng ẩn danh đăng nhập vào ứng dụng này.
- signInWithCustomToken(String) – Đăng nhập người dùng bằng Custom Token nhận được. Sử dụng method này sau khi bạn nhận Firebase Auth Custom Token từ server để đăng nhập người dùng vào hệ thống Xác minh của Firebase.
 
Khi đã thiết lập xong chức năng đăng nhập, ta sẽ tiếp tục tiếng tới Sign Up (đăng ký).
Điều chỉnh SignUpActivity như sau:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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 > < authresult > ( )   {                                  @ Override                                  public   void   onComplete ( @ NonNull  Task < / authresult > < 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 ( ) ;                                      }                                  }                              } ) ;                  }              }          } ) ;      } } | 
Method createUserWithEmailAndPassword() sẽ tạo tài khoản người dùng mới với địa chỉ email và password được cung cấp. Nếu thành công, method này còn tự động đăng nhập người dùng mới vào ứng dụng. Task của AuthResult được trả lại với kết quả của thao tác. Bạn sẽ kiểm tra xem quá trình đăng ký, nếu thành công người dùng sẽ được điều hướng đến MainActivity, nếu không error message sẽ xuất hiện. Trong ứng dụng, bạn có thể xem trước exception được thrown để quyêt định error message hiển thị đến người dùng.
- FirebaseAuthWeakPasswordException được thrown nếu password không đủ mạnh
- FirebaseAuthInvalidCredentialsException được throw nếu địa chỉ email bị sai lệch
- FirebaseAuthUserCollisionException được thrown nếu đã tồn tại tài khoản cho email được cung cấp
Chạy ứng dụng. Nếu bạn nhấn vào Sign Up text view trên Log In view, Sign Up view sẽ mở ra.

Đăng ký một tài khoản. Theo mặc định, firebase sẽ thực hiện một số bước xác minh lên dữ liệu. Ví dụ, password của bạn phải dài ít nhất 6 ký tự.

Nếu vẫn cứng đầu, bạn sẽ thấy lỗi sau.

Nếu đăng ký thành công, bạn sẽ được điều hướng về MainActivity. Nếu nhìn vào Firebase console, bạn có thể thấy được người đùng được tạo dưới Auth > Users.

Firebase team đã xây dựng một thư viện mang tên FirebaseUI giúp đơn giản quá trình thêm Xác minh và kết nối các nhân tố UI vào Firebase database.
Xác minh và xác nhận dữ liệu
Xác định người dùng chỉ là một phần trong bảo mật mà thôi. Khi bạn đã biết được người dùng là ai, bạn cần tìm cách để kiểm soát khả năng truy cập trong Firebase database.
Firebase có một loại ngôn ngữ khai báo cho một số quy luật cụ thể trú trong Firebase server và xác định bảo mật của ứng dụng. Bạn có thể edit các quy luật này trong tab Database > Rules.
Quy luật bảo mật cho phép bạn kiểm soát truy cập vào mỗi phần của database. Theo mặc định, Firebase có cung cấp một số luật quy luật yêu cầu user phải được xác minh trước đó.
| 1 2 3 4 5 6 7 8 | {    "rules" :   {      ".read" :   "auth != null" ,      ".write" :   "auth != null"    } } | 
Firebase Database Rules có cú pháp giống JavaScript và tuân theo 4 loại:
- .read – Miêu tả liệu người dùng có được đọc dữ liệu hay không và đọc khi nào
- .write – Miêu tả liệu có được việt dữ liệu hay không và viết khi nào
- .validate – Xác định hình thái của giá trị được định dạng đúng cách, kể cả có thuộc tính con, và kiểu dữ liệu.
- .indexOn – Chỉ định một thuộc tính con để index và để hỗ trợ sắp xếp và truy vấn
Các quy luật .read và .write xếp theo tầng, vì vậy ruleset sau sẽ cấp quyền đọc đến bất cứ dữ liệu này tại đường dẫn /foo/ (cũng có thể gọi là node foo) cũng như các đường dẫn sâu hơn như /foo/bar/baz. Nên lưu ý, các quy luật .read và .write nông hơn trong database sẽ override các quy luật sâu hơn, vậy nên trong ví dụ này, quyền đọc đến /foo/bar/baz vẫn sẽ được cấp ngay cả khi quy luật tại đường dẫn /foo/bar/baz được đánh giá là false.
| 1 2 3 4 5 6 7 8 9 10 |   {    "rules" :   {      "foo" :   {        ".read" :   true ,        ".write" :   false      }    } } | 
Các luật .validate không phân lớp.
Tinh chỉnh các quy luật như dưới đây và nhấn Publish.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | {    "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"              }            }          }        }      }    } } | 
Ở các quy luật trên, auth != null && auth.uid == $uid giới hạn quyền đọc viết dữ liệu trên node user (cũng như các node con) đến người dùng có uid khớp với id của người dùng đã login (auth.uid). $uid là biến giữ giá trị tại node đó mà không phải chính tên node. Với quy luật này, người dùng sẽ không chỉ cần được xác minh để đọc và viết bất cứ dữ liệu nào lên node và node con, mà còn chỉ có tiếp cận và dữ liệu của chính mình.
Firebase Database Rules bao gồm các biến built-in và functions cho phép bạn chỉ đến các đường dẫn, server-side timestamps, thông tin xác minh khác,… Bạn có thể sử dụng những biến và hàm này để xây dựng các quy luật phức tạp. Những biến và hàm này mang lại sự mạnh mẽ và linh hoạt, cho phép bạn chỉ đến các đường dẫn, server-side timestamps khác,…
Trong apps rules, hãy sử dụng biến built-in auth. Biến này được populate sau khi người dùng xác minh. Nó có chứa các dữ liệu về người dùng, bao gồm auth.uid (identifier chữ số đơn nhất, hoạt động khắp các provider).
Các biến dùng được:
- now – Thời gian hiện tại tính từ thời đại Linux, theo mili giây; rất phù hợp để xác nhận timestamps được tạo với firebase.database.ServerValue.TIMESTAMPcủa SDK.
- root – RuleDataSnapshot thể hiện root path trong Firebase database dúng như trước khi thao tác.
- newData – RuleDataSnapshot thể hiện dữ liệu đúng như trước khi thao tác. Nó bao gồm dữ liệu mới viết và dữ liệu đã có.
- data – RuleDataSnapshot thể hiện dữ liệu đúng như trước khi thực hiện thao tác.
- $variables – đường dẫn wildcard dùng để thể hiện ids và dynamic child keys.
- auth – Thể hiện token payload của người dùng đã xác minh.
Firebase lưu trữ dữ liệu theo định dạng JSON. Trong database của ta, mõi người dùng sẽ có một dãy to-do items mang tên items. Mỗi item sẽ có một title. Ở trên, bạn đảm bảo dữ liệu được viết vào /users//items bằng cách thêm data validation là string có nhiều hơn 0 ký tự. Bởi vậy, một item có title trống sẽ không được lưu.
Hãy chắc chắn bạn đã nhấp vào nút Publish, nếu không các quy luật sẽ không được lưu. Bạn sẽ thấy một message “Rules published”.
Validation rules rất hay nhưng vẫn chưa thế thay thế data validation code trong ứng dụng. Bạn vẫn nên xác minh input trong ứng dụng để cải thiện hiệu năng.
Lưu nhận dữ liệu
Thêm các biến sau vào MainActivity:
| 1 2 3 4 | private   DatabaseReference  mDatabase ; private   String   mUserId ; | 
Sau đó thay đổi onCreate() như sau:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | @ Override protected   void   onCreate ( Bundle  savedInstanceState )   {      super . onCreate ( savedInstanceState ) ;      setContentView ( R . layout . activity_main ) ;      Toolbar  toolbar   =   ( Toolbar )   findViewById ( R . id . toolbar ) ;      setSupportActionBar ( toolbar ) ;      // Khới động Firebase Auth và Database Reference      mFirebaseAuth   =   FirebaseAuth . getInstance ( ) ;      mFirebaseUser   =   mFirebaseAuth . getCurrentUser ( ) ;      mDatabase   =   FirebaseDatabase . getInstance ( ) . getReference ( ) ;      if   ( mFirebaseUser   ==   null )   {          // Chưa đăng nhập, khới động 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 ) ;          // Thêm items thông qua Button và EditText ở cuối 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 ( "" ) ;              }          } ) ;          // Dùng Firebase để populate 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 )   {              }          } ) ;      } } | 
Kế tiếp, hãy tạo một reference đến root node của database với FirebaseDatabase.getInstance().getReference(). Rồi set listener lên nút Add New Item để lưu dữ liệu đến Firebase khi được nhấp vào.
Có bốn method viết dữ liệu vào Firebase Realtime Database:
- setValue() – Viết hoặc thay thế đến một đường dẫn đã xác định, như users/<user -id>/<username>.
- push() – Thêm vào list dữ liệu. Mỗi khi bạn call push(), Firebase sẽ tạo một unique key, còn có thể sử dùng làm identifier đơn nhất, nhưuser-posts/<user -id>/<unique -post-id>.
- updateChildren() – Cập nhật một vài key cho đường dẫn đã xác định mà không cần phải thay thế tất cả dữ liệu.
- runTransaction() – Cập nhật dữ liệu phức tạp có nguy cơ bị corrupt bởi concurrent updates (cập nhật trùng lấp).
Trong code, dữ liệu được lưu với :
| 1 2 3 | mDatabase . child ( "users" ) . child ( mUserId ) . child ( "items" ) . push ( ) . child ( "title" ) . setValue ( text . getText ( ) . toString ( ) ) ; | 
.child sẽ nhận reference đến node xác định nếu đã có, hoặc tạo mới nếu không. Đoạn code trên sẽ lưu đoạn text đã nhập tại đường dẫn /users/<user id>/items/<item id>/title. .push() dùng key đơn nhất để tạo vị trí con mới. Bạn sẽ dùng .push() để tạo key đơn nhất cho mỗi item được thêm vào. .setValue() viết hoặc thay thế dữ liệu đến đường dẫn đã xác định.
Để nhận dữ liệu từ Firebase, hãy thêm một listener vào database reference với addChildEventListener(). Bạn có thể listen các kiểu event nhận dữ liệu sau:
- ValueEventListener: onDataChange() Đọc và listen các thay đổi đến toàn bộ nội dung của một đường dẫn.
- ChildEventListener: onChildAdded() –nhận một list item hoặc listen các phần thêm vào list items. Dùng với onChildAdded()andonChildRemoved()dể theo dõi các thay đổi lên list.
- ChildEventListener: onChildRemoved() – Listen các item được xóa khỏi một list. Dùng với onChildAdded()andonChildChanged()để theo dõi các thay đổi đến list.
- ChildEventListener: onChildMoved() – Listen các thay đổi đến thứ tự của item trong list đã sắp xếp. Các event onChildMoved()luôn đi theo eventonChildChanged()khiến thứ tự của item thay đổi (dựa trên cách thức sắp xếp mà bạn đang sử dụng).
Ứng dụng mẫu của chúng ta chỉ cho phép item được loại bỏ hoặc thêm vào list, nên hãy dùng riêng onChildRemoved() và onChildAdded().
Listener nhận một DataSnapshot(snapshot của data). Snapshot là một image của data tại một vị trí cụ thể trong Firebase database tại một điểm thời gian. Call getValue() lên một snapshot sẽ trả kết quả là object Java đại diện cho dữ liệu. Các kiểu mà getValue() trả được gồm Boolean, String, Long, Double, Map<string , Object>, và List<object>. Nếu không tồn tại dữ liệu tại vị trí, sapshot sẽ trả kết quả null và bạn nên kiểm tra xem có null hay không trước khi sử dụng dữ liệu. Cuối cùng, hãy thêm phần nhận dữ liệu vào list view.
Nếu bạn chạy ứng dụng và thêm một số item, chúng sẽ được thêm vào list và vào database.


Bạn có thể thêm data từ Firebase console bằng cách nhấp vào green + control trên một node và nhập dữ liệu vào. Hãy đảm bảo bạn nhập dữ liệu theo đúng định dạng, nếu không ứng dụng Android sẽ crash khi cố đọc những dữ liệu này. Trong production app, bạn nên thêm validation để đảm bảo ứng dụng không crash khi nhận sai dữ liệu.
Để thêm một item từ Console, nhấp the + on node items và thêm đoạn sau vào trường name. Bạn phải nhập đường dẫn đầy dủ //title.
| 1 2 3 | / 123 / title | 
Thêm “This was added using the console” vào trường value và nhấp Save.
Item sẽ được thêm vào dữ liệu và nếu bạn nhìn vào ứng dụng Android, danh sách sẽ tự cập nhật item vào đó.


Ứng dụng mẫu ta đang xây dựng sẽ gửi một String đến server. Cũng tốt, nhưng với ứng dụng phức tạp hơn, model objects của bạn sẽ phức tạp hơn.
Firebase cho phép bạn chuyển custom Java object sang DatabaseReference.setValue() đọc dữ liệu vào một object với DataSnapshot.getValue(), giả sử class xác định object này có constructor mặc định không nhận argument và public getter để properties được assign.
Để xét rõ hơn trong thực tế, tạo một class tên Item và chuyển thành:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 ;      } } | 
Thay đổi nút Add New Item trên click listener sang:
| 1 2 3 4 5 6 7 8 9 | 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 ( "" ) ;      } } ) ; | 
Thay đổi này sử dụng object Item để lưu dữ liệu vào database. Nội dung của Item được map vào các vị trí con theo dạng lưới. Chạy ứng dụng và bạn sẽ vẫn có thể thêm dữ liệu vào list và xem dữ liệu đã lưu trên console server.
Xóa dữ liệu
Ứng dụng của bạn giờ đây có thể lưu trữ và truy xuất dữ liệu để populate một list view. Kế tiếp ứng dụng cần cho phép người dùng xóa item từ list và Firebase.
Thêm đoạn sau vào cuối block else tron onCreate() trong class MainActivity:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Xóa item khi nhấp 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 )   {                      }                  } ) ;      } } ) ; | 
Đoạn code sẽ set onclick listener lên list view và truy vấn database khi một item được nhấp vào. Đồng thời tìm kiếm item trong Firebase database với title giống với string tại vị trí được nhấp. Với ứng dụng phức tạp hơn, bạn nên tìm kiếm theo tiêu chuẩn đặc trưng hơn làn object, như id chẳng hạn. Nó sau đó sẽ loại bỏ lần xuất hiện đầu tiên của item từ database. Listview được cập nhật tự.
Chạy ứng dụng và bạn sẽ có thể xóa item từ Firebase bằng cách nhấp lên đó trên ứng dụng Android.
Logging Users Out
Việc call signout() sẽ làm mất hiệu lực của token của người dùng, và đăng thoát họ khỏi ứng dụng. Thay đổi các dòng sau trong onOptionsItemSelected(), làm đăng thoát người dùng và điều hướng về Login view.
| 1 2 3 4 5 6 | if   ( id   ==   R . id . action_logout )   {      mFirebaseAuth . signOut ( ) ;      loadLogInView ( ) ; } | 
- Những trang gọi vốn cộng đồng ( crowdfunding ) không phải ai cũng biết
- Sử dụng Mạng xã hội để tạo giá trị cho start-up ?
- B2B CLOUD
- Mua vé xe, đặt vé xe, vé máy bay
- Thiết bị di động chiếm 20% lưu lượng mua sắm trực tuyến
- Mã nguồn email marketing dùng Interspire Email Marketer kèm theo hướng dẫn dùng
- SEO mũ đen và SEO mũ trắng
- Mã nguồn website công ty phần mềm ERP
- Một coder không có kĩ năng design cũng có thể tạo ra ứng dụng Web với 32 công cụ tiện lợi sau
- Con đường sự nghiệp của một lập trình viên
- Top 10 nguồn học JavaScript trực tuyến tốt nhất
- App tìm kiếm nhạc Vietmusic trên Blackberry
DVMS chuyên:
  - Tư vấn, xây dựng, chuyển giao công nghệ Blockchain, mạng xã hội,...
  - Tư vấn ứng dụng cho smartphone và máy tính bảng, tư vấn ứng dụng vận tải thông minh, thực tế ảo, game mobile,...
  - Tư vấn các hệ thống theo mô hình kinh tế chia sẻ như Uber, Grab, ứng dụng giúp việc,...
  - Xây dựng các giải pháp quản lý vận tải, quản lý xe công vụ, quản lý   xe doanh nghiệp, phần mềm và ứng dụng logistics, kho vận, vé xe điện   tử,...
  - Tư vấn và xây dựng mạng xã hội, tư vấn giải pháp CNTT cho doanh nghiệp, startup,...
Vì sao chọn DVMS?
  - DVMS nắm vững nhiều công nghệ   phần mềm, mạng và viễn thông. Như Payment gateway, SMS gateway, GIS,   VOIP, iOS, Android, Blackberry, Windows Phone, cloud computing,…
  - DVMS có kinh nghiệm triển khai các hệ thống trên các nền tảng điện toán đám mây nổi tiếng như Google, Amazon, Microsoft,…
  - DVMS có kinh nghiệm thực tế tư vấn, xây dựng, triển khai, chuyển giao,   gia công các giải pháp phần mềm cho khách hàng Việt Nam, USA, Singapore,   Germany, France, các tập đoàn của nước ngoài tại Việt Nam,…
  
  Quý khách xem Hồ sơ năng lực của DVMS tại đây >> 
  
  Quý khách gửi yêu cầu tư vấn và báo giá tại đây >>









