Trước khi bắt đầu
Trước khi có thể sử dụng Realtime Database, bạn cần:
Đăng ký dự án Unity của bạn và định cấu hình dự án đó để sử dụng Firebase.
Nếu dự án Unity của bạn đã sử dụng Firebase, thì tức là dự án đó đã được đăng ký và định cấu hình cho Firebase.
Nếu chưa có dự án Unity, bạn có thể tải ứng dụng mẫu xuống.
Thêm Firebase Unity SDK (cụ thể là
FirebaseDatabase.unitypackage) vào dự án Unity của bạn.
Xin lưu ý rằng việc thêm Firebase vào dự án Unity bao gồm các nhiệm vụ trong cả bảng điều khiển Firebase và trong dự án Unity đang mở (ví dụ: bạn tải tệp cấu hình Firebase xuống từ bảng điều khiển, sau đó di chuyển các tệp đó vào dự án Unity).
Đang lưu dữ liệu
Có 5 phương thức để ghi dữ liệu vào Firebase Realtime Database:
| Phương thức | Các cách dùng phổ biến |
|---|---|
SetValueAsync() |
Ghi hoặc thay thế dữ liệu vào một đường dẫn đã xác định, chẳng hạn như users/<user-id>/<username>. |
SetRawJsonValueAsync() |
Ghi hoặc thay thế dữ liệu bằng JSON thô, chẳng hạn như users/<user-id>/<username>. |
Push() |
Thêm vào danh sách dữ liệu. Mỗi lần bạn gọi Push(), Firebase sẽ tạo một khoá duy nhất cũng có thể dùng làm giá trị nhận dạng riêng biệt, chẳng hạn như user-scores/<user-id>/<unique-score-id>. |
UpdateChildrenAsync() |
Cập nhật một số khoá cho một đường dẫn đã xác định mà không thay thế tất cả dữ liệu. |
RunTransaction() |
Cập nhật dữ liệu phức tạp có thể bị hỏng do các bản cập nhật đồng thời. |
Lấy DatabaseReference
Để ghi dữ liệu vào Cơ sở dữ liệu, bạn cần có một thực thể của DatabaseReference:
using Firebase; using Firebase.Database; public class MyScript: MonoBehaviour { void Start() { // Get the root reference location of the database. DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference; } }
Ghi, cập nhật hoặc xoá dữ liệu tại một tệp đối chiếu
Thao tác ghi cơ bản
Đối với các thao tác ghi cơ bản, bạn có thể sử dụng SetValueAsync() để lưu dữ liệu vào một tệp đối chiếu được chỉ định, thay thế mọi dữ liệu hiện có tại đường dẫn đó. Bạn có thể sử dụng phương thức này để truyền các kiểu tương ứng với các kiểu JSON có sẵn như sau:
stringlongdoubleboolDictionary<string, Object>List<Object>
Nếu sử dụng đối tượng C# đã nhập, bạn có thể dùng JsonUtility.ToJson() tích hợp sẵn để chuyển đổi đối tượng thành JSON thô và gọi SetRawJsonValueAsync().
Ví dụ: bạn có thể có một lớp User như sau:
public class User { public string username; public string email; public User() { } public User(string username, string email) { this.username = username; this.email = email; } }
Bạn có thể thêm người dùng bằng SetRawJsonValueAsync() như sau:
private void writeNewUser(string userId, string name, string email) { User user = new User(name, email); string json = JsonUtility.ToJson(user); mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json); }
Việc sử dụng SetValueAsync() hoặc SetRawJsonValueAsync() theo cách này sẽ ghi đè dữ liệu
tại vị trí đã chỉ định, bao gồm cả mọi nút con. Tuy nhiên, bạn vẫn có thể cập nhật một phần tử con mà không cần viết lại toàn bộ đối tượng. Nếu muốn cho phép người dùng cập nhật hồ sơ, bạn có thể cập nhật tên người dùng như sau:
mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);
Thêm vào danh sách dữ liệu
Sử dụng phương thức Push() để thêm dữ liệu vào danh sách trong các ứng dụng nhiều người dùng.
Phương thức Push() tạo một khoá duy nhất mỗi khi một phần tử con mới được thêm vào tệp đối chiếu Firebase đã chỉ định. Bằng cách sử dụng các khoá được tạo tự động này cho mỗi phần tử mới trong danh sách, một số ứng dụng có thể thêm phần tử con vào cùng một vị trí cùng lúc mà không xảy ra xung đột ghi. Khoá duy nhất do Push() tạo dựa trên dấu thời gian, vì vậy, các mục trong danh sách sẽ tự động được sắp xếp theo thứ tự thời gian.
Bạn có thể sử dụng tệp đối chiếu với dữ liệu mới do phương thức Push() trả về để lấy giá trị của khoá được tạo tự động của phần tử con hoặc đặt dữ liệu cho phần tử con. Khi gọi Key trên tệp đối chiếu Push(), giá trị của khoá được tạo tự động sẽ được trả về.
Cập nhật các trường cụ thể
Để ghi đồng thời vào các phần tử con cụ thể của một nút mà không ghi đè lên các nút con khác, hãy sử dụng phương thức UpdateChildrenAsync().
Khi gọi UpdateChildrenAsync(), bạn có thể cập nhật các giá trị con ở cấp thấp hơn bằng cách chỉ định đường dẫn cho khoá. Nếu dữ liệu được lưu trữ ở nhiều vị trí để mở rộng quy mô tốt hơn, bạn có thể cập nhật tất cả các phiên bản của dữ liệu đó bằng cách sử dụng tính năng phân phối dữ liệu. Ví dụ: một trò chơi có thể có lớp LeaderboardEntry như sau:
public class LeaderboardEntry { public string uid; public int score = 0; public LeaderboardEntry() { } public LeaderboardEntry(string uid, int score) { this.uid = uid; this.score = score; } public Dictionary<string, Object> ToDictionary() { Dictionary<string, Object> result = new Dictionary<string, Object>(); result["uid"] = uid; result["score"] = score; return result; } }
Để tạo một LeaderboardEntry và đồng thời cập nhật mục đó vào nguồn cấp dữ liệu điểm số gần đây và danh sách điểm số của chính người dùng, trò chơi sẽ sử dụng mã như sau:
private void WriteNewScore(string userId, int score) { // Create new entry at /user-scores/$userid/$scoreid and at // /leaderboard/$scoreid simultaneously string key = mDatabase.Child("scores").Push().Key; LeaderBoardEntry entry = new LeaderBoardEntry(userId, score); Dictionary<string, Object> entryValues = entry.ToDictionary(); Dictionary<string, Object> childUpdates = new Dictionary<string, Object>(); childUpdates["/scores/" + key] = entryValues; childUpdates["/user-scores/" + userId + "/" + key] = entryValues; mDatabase.UpdateChildrenAsync(childUpdates); }
Ví dụ này sử dụng Push() để tạo một mục nhập trong nút chứa các mục nhập cho tất cả người dùng tại /scores/$key, đồng thời truy xuất khoá bằng Key. Sau đó, bạn có thể dùng khoá này để tạo mục nhập thứ hai trong điểm số của người dùng tại /user-scores/$userid/$key.
Bằng cách sử dụng các đường dẫn này, bạn có thể thực hiện cập nhật đồng thời cho nhiều vị trí trong cây JSON bằng một lệnh gọi duy nhất đến UpdateChildrenAsync(), chẳng hạn như cách ví dụ này tạo mục nhập mới ở cả hai vị trí. Các bản cập nhật đồng thời được thực hiện theo cách này là không thể phân chia: tất cả các bản cập nhật đều thành công hoặc tất cả các bản cập nhật đều không thành công.
Xóa dữ liệu
Cách đơn giản nhất để xoá dữ liệu là gọi RemoveValue() trên một tệp đối chiếu đến vị trí của dữ liệu đó.
Bạn cũng có thể xoá bằng cách chỉ định null làm giá trị cho một thao tác ghi khác, chẳng hạn như SetValueAsync() hoặc UpdateChildrenAsync(). Bạn có thể sử dụng kỹ thuật này với UpdateChildrenAsync() để xoá nhiều phần tử con trong một lệnh gọi API.
Biết thời điểm dữ liệu của bạn được cam kết.
Để biết thời điểm dữ liệu của bạn được cam kết với máy chủ Firebase Realtime Database, bạn có thể thêm một phần tiếp theo. Cả SetValueAsync() và UpdateChildrenAsync() đều trả về một Task cho phép bạn biết khi nào thao tác hoàn tất. Nếu lệnh gọi không thành công vì bất kỳ lý do gì, thì Tasks IsFaulted sẽ là true và thuộc tính Exception sẽ cho biết lý do không thành công.
Lưu dữ liệu dưới dạng giao dịch
Khi làm việc với dữ liệu có thể bị hỏng do các nội dung sửa đổi đồng thời, chẳng hạn như bộ đếm gia tăng, bạn có thể sử dụng thao tác giao dịch.
Bạn đặt cho thao tác này một Func. Thao tác cập nhật này Func lấy trạng thái hiện tại của dữ liệu làm đối số và trả về trạng thái mới mà bạn muốn ghi. Nếu một ứng dụng khác ghi vào vị trí trước khi giá trị mới của bạn được ghi thành công, thì hàm cập nhật của bạn sẽ được gọi lại bằng giá trị hiện tại mới và quá trình ghi sẽ được thử lại.
Ví dụ: trong một trò chơi, bạn có thể cho phép người dùng cập nhật bảng xếp hạng với năm điểm số cao nhất:
private void AddScoreToLeaders(string email, long score, DatabaseReference leaderBoardRef) { leaderBoardRef.RunTransaction(mutableData => { List<object> leaders = mutableData.Value as List<object> if (leaders == null) { leaders = new List<object>(); } else if (mutableData.ChildrenCount >= MaxScores) { long minScore = long.MaxValue; object minVal = null; foreach (var child in leaders) { if (!(child is Dictionary<string, object>)) continue; long childScore = (long) ((Dictionary<string, object>)child)["score"]; if (childScore < minScore) { minScore = childScore; minVal = child; } } if (minScore > score) { // The new score is lower than the existing 5 scores, abort. return TransactionResult.Abort(); } // Remove the lowest score. leaders.Remove(minVal); } // Add the new high score. Dictionary<string, object> newScoreMap = new Dictionary<string, object>(); newScoreMap["score"] = score; newScoreMap["email"] = email; leaders.Add(newScoreMap); mutableData.Value = leaders; return TransactionResult.Success(mutableData); }); }
Việc sử dụng giao dịch giúp ngăn bảng xếp hạng không chính xác nếu nhiều người dùng ghi điểm cùng một lúc hoặc ứng dụng có dữ liệu cũ. Nếu giao dịch bị từ chối, máy chủ sẽ trả về giá trị hiện tại cho ứng dụng, sau đó ứng dụng sẽ chạy lại giao dịch với giá trị đã cập nhật. Quá trình này lặp lại cho đến khi giao dịch được chấp nhận hoặc đã có quá nhiều lần thử.
Ghi dữ liệu khi không có mạng
Nếu ứng dụng khách mất kết nối mạng, ứng dụng của bạn sẽ tiếp tục hoạt động bình thường.
Mỗi ứng dụng khách kết nối với cơ sở dữ liệu Firebase đều duy trì phiên bản nội bộ riêng của mọi dữ liệu đang hoạt động. Khi dữ liệu được ghi, dữ liệu đó sẽ được ghi vào phiên bản cục bộ này trước tiên. Sau đó, ứng dụng Firebase sẽ đồng bộ hoá dữ liệu đó với máy chủ cơ sở dữ liệu từ xa và với các ứng dụng khác trên cơ sở "nỗ lực tối đa".
Do đó, tất cả các hoạt động ghi vào cơ sở dữ liệu đều kích hoạt các sự kiện cục bộ ngay lập tức, trước khi bất kỳ dữ liệu nào được ghi vào máy chủ. Điều này có nghĩa là ứng dụng của bạn vẫn có thể phản hồi bất kể độ trễ mạng hoặc khả năng kết nối.
Sau khi kết nối được thiết lập lại, ứng dụng của bạn sẽ nhận được một nhóm sự kiện thích hợp để ứng dụng đồng bộ hoá với trạng thái máy chủ hiện tại mà không cần phải viết mã tuỳ chỉnh.