Tối ưu hóa ứng dụng Android bằng ArrayMap và SparseArray

0
504

Android App Optimization Using ArrayMap and SparseArray

Trong bài viết này, mình sẽ chỉ cho bạn tại sao và khi nào sử dụng ArrayMap và SparseArray để tối ưu hóa ứng dụng Android một cách hiệu quả.

Bất cứ khi nào bạn cần lưu trữ dạng Key => value, có lẽ HashMap là kiểu đầu tiên bạn nghĩ tới phải không? Hashmap khá linh hoạt, sử dụng được ở mọi nơi mà chúng ta lại không cần bận tâm quá nhiều về “tác dụng phụ” của nó. Có một vấn đề thường thấy là khi bạn cố dùng thử Hashmap trong Android Development IDE (Android Studio), nó sẽ đưa ra lời cảnh báo rằng bạn nên sử dụng ArrayMap để thay thế Hashmap. Bạn có biết tại sao không?

>>> Cách sử dụng Android studio cho bạn

Tối ưu hóa ứng dụng Android qua 2 công cụ

Tiếp đến, mình sẽ chỉ cho bạn biết khi nào chúng ta nên sử dụng ArrayMap và cách thức hoạt động của cấu trúc dữ liệu này.

HashMap và ArrayMap

HashMap nằm trong gói java.utils.HashMap, trong khi ArrayMap nằm trong gói android.support.v4.util.ArrayMap. Gói support.v4 có thể hỗ trợ những phiên bản android đời cũ hơn.

Đây là video có nói rất chi tiết trên kênh Android Developers, bạn có thể tham khảo thêm ở đây(Rất tiếc video bằng tiếng Anh chưa có sub nên các bạn cố gắng vừa học tiếng anh vừa học Android nhé).

ArrayMap là kiểu dữ liệu cấu trúc dạng generic key => value được thiết kế nhằm tối ưu hóa ứng dụng Android bộ nhớ hơn lưu trữ trên HashMap truyền thống.

ArrayMap tạo một ánh xạ giữa một mảng dữ liệu – một mảng số nguyên của  hash codes cho mỗi item và một mảng Object của key ” value. Điều này tránh yêu cầu phải tạo ra thêm Object khi thêm entry vào map. ArrayMap cũng kiếm soát kích thước nhanh gọn hơn, vì chúng ta chỉ cần sao chép các entry trong mảng mà không phải xây dựng lại hash map.

Lưu ý: không dùng ArrayMap cho cấu trúc dữ liệu có số lượng item lớn. Nhìn chung, nó chậm hơn HashMap truyền thống vì việc yêu cầu dùng thuật toán tìm kiếm nhị phân(binary search) để tra cứu. Thêm và xóa đều yêu cầu phải chèn và xóa các mảng entry.

Với các tập lưu trữ lên đến hàng trăm phần tử thì sự chênh lệch về hiệu suất dưới 50%. Theo cá nhân mình thì con số này không đáng kể.

So tài HashMap với ArrayMap

HashMap

Về cơ bản, Hashmap là một mảng của HashMap.Entry (Entry là lớp bên trong của Hashmap).

Nếu nhìn tổng quan thì một instance của lớp Entry là:

  • 1 non-primitive key
  • 1 non-primitive value
  • Hashcode của một Object
  • trỏ đến Entry tiếp theo

Vậy điều gì sẽ xảy ra khi key => value được chèn vào một HashMap?

  • Chúng ta tính hashCode của key và gán giá trị đó cho biến hashCode của EntryClass.
  • Sau đó, chúng ta sử dụng hashCode để lẩy được index của bucket nơi mà nó lưu trữ.
  • Nếu bucket tồn tại, phần lưu trữ mới sẽ được chèn vào vị trí cuối cùng và được chỉ tới 1 bucket mới tạo thành một danh sách bucket liên kết.

P/s: Bucket là nơi lưu trữ các key có hash gần như nhau. bucket được lưu trong một array nên chi phí truy xuất chỉ là O(1).

bucket

 

Bây giờ, khi bạn truy xuất HashMap để tìm giá trị của 1 key thì chi phí truy xuất là O(1). Nhưng quan trọng nhất là bộ nhớ càng nhiều, chi phí càng cao.

Nhược điểm:

  • Autoboxing có nghĩa là mỗi lần chèn sẽ tạo ra đối tượng thừa. Điều này ảnh hưởng đến cả việc sử dụng bộ nhớ, cũng như thu gom rác.
  • Bản thân HashMap.Entry là một lớp thêm của các đối tượng được tạo ra và thu gom rác(garbage collection).
  • Các Buckets được sắp xếp lại mỗi lần HashMap xóa hoặc thêm phần tử. Hoạt động này rất tốn kém để có thể phát triển số lượng các objects.

Trong Android, khi nói đến ứng dụng kiểu responsive (Ứng dụng hiểu thị tương thích với mọi loại màn hình từ phone đến tablet) thì bộ nhớ rất quan trọng. Việc phân bổ và giải phóng bộ nhớ liên tục cùng với việc thu gom rác (garbage collection) sẽ làm chậm ứng dụng Android của bạn.

Hãy nhớ rằng – thu gom rác (garbage collection) trên ứng dụng cũng phải tốn tài nguyên nhất định

Khi garbage collection hoạt động, ứng dụng của bạn không thể chạy,  điều đó dẫn đến bị thiết bị chạy chậm hơn.

ArrayMap

Vậy để tối ưu hóa ứng dụng android thì ArrayMap có gì khác? ArrayMap sử dụng 2 mảng. Các biến thực thể được sử dụng bên trong là Object [] mArray để lưu trữ các đối tượng và int [] mHashes để lưu trữ hashCodes. Khi một cặp key -> value được chèn vào:

  • key => value pair được chèn để tạo đối tượng.
  • Đối tượng key này được chèn ở mục có sẵn bên cạnh trong mArray[ ].
  • Value của đối tường cũng được chèn ở mục key có sẵn tiếp theotrong mArray[ ].
  • hashCode của key được tính toán và đặt tại mHashes[ ] ở mục có sẵn tiếp theo.

Khi tìm key, bạn cần:

  • Tính hashCode của key.
  • Tìm Binary cho hashCode trên ở mảng mHashes. Time complexitycó thể tăng tới O(logN).
  • Khi gặp 1 danh sách hash, chúng ta cần biết rằng key ở mục 2*index trong mArra và ở mục2*index+1.

Tại đây, time complexity từ O(1) đã lên tới O(logN)

Đề xuất những cấu trúc data:

  • ArrayMap<K,V> thay cho HashMap<K,V>
  • ArraySet<K,V> thay cho HashSet<K,V>
  • SparseArray<V> thay cho HashMap<Integer,V>
  • SparseBooleanArray thay cho HashMap<Integer,Boolean>
  • SparseIntArray thay cho HashMap<Integer,Integer>
  • SparseLongArray thay cho HashMap<Integer,Long>
  • LongSparseArray<V> thay cho HashMap<Long,V>

Hy vọng qua bài viết trên, bạn có thể áp dụng ngay tối ưu hóa ứng dụng Android một cách thành công. Có điều gì thắc mắc hoặc góp ý thì comment ngay bên dưới nhé!

Bình luận. Đặt câu hỏi cũng là một cách học

avatar
  Theo dõi bình luận  
Thông báo