Deal with wrong things when using Realm

Realm là gì ?

Realm là một cơ sở dữ liệu di động và là một sự thay thế cho SQLite. Mặc dù là một cơ sở dữ liệu OO nhưng nó có một số khác biệt với các cơ sở dữ liệu khác. Realm không hoạt động giống như SQLite. Thay vào đó, nó được xây dựng bằng C++ và nhằm cung cấp một giải pháp thay thế cho SQLite. Realm lưu trữ dữ liệu trong một định dạng phổ bảng dựa trên bảng bởi một lõi C ++. Đây là những gì cho phép Realm cho phép truy cập dữ liệu từ nhiều ngôn ngữ cũng như một loạt các truy vấn đặc biệt.

Realm được mô tả là:

  • Đơn giản - Dữ liệu sẽ được lưu trữ trong cơ sở dữ liệu dưới dạng Object, vì vậy nó sẽ dễ dàng để lấy dữ liệu từ cơ sở dữ liệu.
  • Nhanh - Realm Database nhanh hơn cơ sở dữ liệu SQLite để thực hiện truy vấn và bảo trì cơ sở dữ liệu.
  • Phát triển theo hướng hiện đại - Realm hỗ trợ các tính năng java hiện đại như Generics, Vectors và công nghệ Swift.

Những lỗi thường gặp khi làm việc với Realm ?

Sử dụng realm.beginTransaction() và realm.commitTransaction() thay vì realm.executeTransaction(Realm.Transaction)

Vấn đề với điều này là executeTransaction() sẽ tự động xử lý việc gọi realm.cancelTransaction() trong trường hợp ngoại lệ bị ném ra, trong khi lựa chọn khác thường bỏ qua điều này.

// Không nên:
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
realm.copyToRealm(dog)
realm.commitTransaction();

// Nên:
Realm realm = null;
try {
    realm = Realm.getDefaultInstance();
    realm.executeTransaction(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            realm.insertOrUpdate(dog);
        }
    });
} finally {
  if(realm != null) {
    realm.close();
  }
}

Mở Realm với Realm.getDefaultInstance() mà họ không bao giờ đóng nó.

Vấn đề với điều này là khi ở background thread, có một ví dụ là Realm được mở mà bạn không đóng ngay khi thực hiện xong transaction thì sẽ gây ra lỗi. Như vậy, bạn nên đóng Realm ở background thread khi thực hiện được thực hiện thao tác xong. Điều này bao gồm cả IntentServices. Bạn cũng nên xem xét đóng Realm trên main thread khi bạn thoát khỏi Acitivty/Fragment.

Thực hiện nhiều transaction ( ví dụ mỗi phần tử được thêm vào một giao dịch mới trong một vòng lặp ) ở background thread.

Nó sẽ gây ra rắc rối tại một số thời điểm. Bạn nên giảm thiểu số lượng transaction ở background thread cho trước.

Lạm dụng realmResults.asObservable() và hiểu nhầm về sự hỗ trợ của RxJava nói chung.

Hầu hết các câu hỏi liên quan đến Rx/Realm ở StackOverflow đều có khuynh hướng trở nên khó hiểu, bởi vì có một sự hiểu lầm cơ bản về việc hỗ trợ Rx của Realm được cho là phải làm gì. Điểm của realmResults.asObservable() là để xem toàn bộ kết quả như một Observable mà không cần phải thêm trình nghe thay đổi theo cách thủ công và được cập nhật (và nhận được thông báo trong trường hợp bảng được thay đổi) trên thread UI.

private Subscription queryDogsByInput() {
        return RxTextView.textChanges(editText)
                         .switchMap(charSequence -> realm.where(Dog.class)
                                                         .contains(DogFields.NAME, charSequence.toString())
                                                         .findAllSortedAsync(DogFields.NAME)
                                                         .asObservable()
                        ).filter(RealmResults::isLoaded)
                         .subscribe(dogs -> adapter.updateData(dogs));
    }

Cố gắng phân trang một RealmResults<T> hoặc "giới hạn số kết quả trong đó" vì không có lý do chính đáng nào.

Hầu hết là do hạn chế RealmResults, bạn chỉ cần không phải lập chỉ mục nó trên ngưỡng tùy ý của bạn. Để làm rõ: RealmResults KHÔNG chứa bất kỳ phần tử nào. Nó chứa các phương tiện để đánh giá các kết quả của truy vấn. Phần tử này thu được từ Realm chỉ khi bạn gọi realmResults.get(i), và chỉ một phần tử duy nhất được trả về cùng một lúc. Nó giống như một con trỏ, ngoại trừ đó là một Danh sách. Vì vậy, "hạn chế" nó và "paginating" nó không có ý nghĩa. Nếu bạn thực sự cần nó, sau đó giới hạn chỉ số của bạn.

Sử dụng findAll().Sort() thay vì findAllSorted()

Sort() trả về một kết quả mới, và bạn phải thêm mỗi lần lắng nghe thay đổi vào kết quả mới. FindAllSorted() giữ thứ tự của nó không có vấn đề gì, và giữ lại người nghe của nó (vì đó là trường hợp RealmResults giống nhau). Vấn đề này là rất phổ biến, thậm chí một ví dụ ở Stack Overflow chỉ để cho thấy rằng bạn không nên làm nó. Nếu bạn thấy findAll().Sort(), nó có thể bị hỏng, và nó nên là findAllSorted().

Không sử dụng annotation @Index trên các trường bạn sử dụng trong truy vấn của mình mặc dù bạn nên.

Nó làm cho truy vấn của bạn nhanh hơn gấp 4 lần. Bạn nên luôn luôn sử dụng @Index nếu bạn sử dụng một trường trong một truy vấn.

Mở một instance Realm ở background thread, có được RealmResults, sau đó bắt đầu transaction, và sau đó thao tác các kết quả ở bên ngoài của transaction.

Khi bạn mở một transaction, bạn đang viết trực tiếp vào phiên bản mới nhất của Realm. Có nghĩa là RealmResults của bạn nên được thu được bên trong transaction, và KHÔNG bên ngoài transaction; Để bạn luôn thấy phiên bản mới nhất.

try(Realm realm = Realm.getDefaultInstance()) {
    final RealmResults<Dog> dogs = realm.where(Dog.class).findAll();
    realm.executeTransaction(inRealm -> {
        for(Dog dog : dogs) {
           //...
        }
    });
}

////////////////////

try(Realm realm = Realm.getDefaultInstance()) {
    realm.executeTransaction(inRealm -> {
        final RealmResults<Dog> dogs = inRealm.where(Dog.class).findAll();
        for(Dog dog : dogs) {
           //...
        }
    });
}

Không sử dụng RealmRecyclerViewAdapterRecyclerView mặc dù nó đã ra được một thời gian.

Cũng cần lưu ý rằng adapter của họ (phiên bản 1.3.0) quản lý việc thêm và xóa một trình lắng nghe thay đổi mà gọi adapter.notifyDataSetChanged() để thực sự tất cả những gì bạn cần làm là cung cấp truy vấn bạn muốn xem các phần tử trên màn hình và nó chỉ Hoạt động và tự động cập nhật khi công cụ xảy ra.

Zero-copy truy vấn đánh giá

Đó là những gì tôi đã nói ở trên về RealmResults<T> không chứa bất kỳ phần tử nào. Bạn chỉ nhận được các lớp proxy tạo ra khi bạn gọi. Get(i). Danh sách bạn nhận được chỉ là một con trỏ nhạt đến cơ sở dữ liệu nằm bên dưới. Đánh giá RealmResults<T> bản thân bản chất là miễn phí và cho phép bạn dễ dàng truy cập vào tất cả các dữ liệu đủ điều kiện cho các điều kiện của bạn mà không sao chép toàn bộ bộ dữ liệu vào bộ nhớ.

Cho phép write từ nhiều thread khác nhau (chỉ một giao dịch cùng một lúc) và cập nhật tự động trên main thread để hiển thị phiên bản mới nhất của dữ liệu.

Thực hiện việc tải dữ liệu ở background thread và dùng event bus để gửi về và cập nhật dữ liệu mới nhất ở list rồi call adapter.notifyDatasetChanged()

Khả năng truy cập dữ liệu đồng bộ nhanh chóng (nếu bạn muốn)

Bạn có thể sử dụng API async nếu muốn nhưng kết quả được đánh giá cho các trường được lập chỉ mục không gây ra bất cứ điều gì ngay cả thông qua API đồng bộ.

Vậy Realm nên được sử dụng như thế nào ?

  • Không sử dụng beginTransaction()commitTransaction() theo cách thủ công. Sử dụng executeTransaction() (hoặc executeTransactionAsync() trên main thread nếu cần).
  • Sử dụng RealmRecyclerViewAdapter với phương thức findAll.Async() để loại bỏ truy vấn hoàn toàn khỏi giao diện người dùng và tự động hiển thị bất kỳ phần tử mới nào được cam kết từ background thread (adapter của Realm xử lý với RealmChangeListener)
  • Không có bản sao thủ công và không yêu cầu đồng bộ hoá, bạn chỉ cần xác định truy vấn và cách kết hợp trình xem với đối tượng Realm và bạn sẽ có giải pháp sử dụng bộ nhớ thấp với tính năng tự động đồng bộ hóa.

Tham khảo và dịch: https://medium.com/@Zhuinden/how-to-use-realm-for-android-like-a-champ-and-how-to-tell-if-youre-doing-it-wrong-ac4f66b7f149#.zaoi1074a