Tối ưu hóa giao diện Android

Xin chào các bạn, hôm nay chúng ta hãy cùng đến với một chủ đề quen mà lạ – tối ưu hóa giao diện Android. Trong khuôn khổ bài viết này xin mạn phép nêu sơ lược một số vấn đề cơ bản về giao diện và vài gợi ý để có thể tối ưu hóa chúng.

Đầu tiên, xin phép thôi không nhắc tới hậu quả của việc không tối ưu hóa giao diện Android nữa, thay vào đó xin nêu ra 1 khái niệm tương đối cơ bản. Tại sao mắt chúng ta thấy ứng dụng này chạy “mượt”, ứng dụng kia thì lại không được như vậy? Nghiên cứu chỉ ra rằng – với mắt của con người – ứng dụng phải hoàn thành mọi công việc trong vòng 16 mili giây hoặc ít hơn cho mỗi lần làm mới màn hình để mang lại cảm giác mượt mà. Con số này ở đâu ra, hãy cùng điểm qua một số thông tin sau đây:
Não bộ thu thập và xử lý thông tin một cách liên tục. Điều này cho phép chúng ta thực hiện một số mánh vui vui, ví dụ như khi những tấm ảnh tĩnh được hiển thị liên tiếp nhau đủ nhanh, chúng ta sẽ nhìn thấy như chúng đang chuyển động vậy. Hình ảnh sau đây là một ví dụ cho điều này: 

  • Ở tốc độ 10-12 trang/giây, chúng ta có thể nhìn thấy chuyển động tương đối rõ ràng, tuy nhiên nhận thức rằng đó chỉ là những trang giấy vẫn còn.
  • Tốc độ 24 trang/ giây cho phép con người có thể nhìn thấy chuyển động (nguyên văn “fluid motion”). Đây cũng là tốc độ chuẩn cho nền công nghiệp phim ảnh, khi vừa đủ nhanh để hình thành chuyển động, vừa không quá ảnh hưởng đến ngân sách làm phim.
  • Tốc độ 30 trang/ giây là đủ nhưng chưa sống động. Nó là đủ cho các bộ phim, tuy nhiên với các cảnh quay đòi hỏi kỹ xảo đặc biệt thì như vậy vẫn chưa đủ.
  • 60 trang/ giây là tốc độ lý tưởng để hầu hết mọi người nhìn thấy các chuyển động rõ nét, mượt mà.

Chỉ số đánh giá số trang trên một giây được gọi là tần số khung hình. Tần số này sẽ ảnh hưởng đến sự mượt mà của chuyển động, tuy nhiên đó chưa phải là tất cả. Mắt con người rất nhạy cảm với chuyển động không đồng nhất. Ví dụ, khi ứng dụng chạy với tần số 60 khung hình trên giây, chỉ cần một khung hình cần nhiều thời gian hơn khung hình trước đó để hiển thị, người dùng sẽ ngay lập tức nhận ra được sự mượt mà bị phá vỡ, hay còn gọi là “giật”(hitching), lag hay junk.

Hầu hết các thiết bị di động hiện đại đều làm mới khung hình ở tần số 60fps (frame-per-second), để đạt được điều này, hệ thống cần vẽ lại hoạt động của ứng dụng sau mỗi 16 mili giây (1000 ms / 60 frames = 16.667 ms). Do đó, ứng dụng sẽ có khoảng 16 mili giây để làm mới mỗi khung hình.

Khoảng thời gian cho mỗi khung hình kể trên đã bao gồm thời gian vẽ, phản hồi intents và xử lý hành động… Nhìn rộng hơn thì ứng dụng của bạn sẽ chia sẽ thời gian 16ms này cho cả các hệ thống phụ khác, vậy nên đừng dự tính chiếm dụng hết quỹ thời gian này. Nếu ứng dụng tiêu tốn hơn 16ms để xử lý 1 khung hình nào đó, chúng ta sẽ có 1 khung hình bị dropped.

Vậy câu hỏi đặt ra, làm sao để phát hiện việc 1 khung hình nào đó bị dropped? May mắn là chúng ta không phải ngồi soi từng khung hình trong quá trình chạy ứng dụng, với sự trợ giúp từ Android Performance Profiling Tools, chúng ta có khả năng để tìm ra và xử lý chúng. Trong khuôn khổ bài viết này, xin phép giới thiệu sơ lược về tool Hierarchy Viewer. Đây là tool có khả năng biểu diễn cấu trúc view trong ứng dụng và tính toán thời gian render cho từng view. Nó có thể dùng để:

  • Đơn giản hóa cấu trúc view để tránh overdraw, đồng thời khiến việc quản lý đơn giản hơn.
  • Tìm những điểm thắt cổ chai tiềm tàng khi render dựa vào cấu tạo và hình dáng của cấu trúc view.

 

Cách sử dụng Hierarchy Viewer

Điều kiện tiên quyết:
a. Thiết bị di động để hoạt động với Hierarchy Viewer:
– Enable Developer Option. (khuyến cáo sử dụng device thật để có kết quả chính xác hơn)
– Cài đặt ANDROID_HVPROTO trên môi trường desktop kết nối với thiết bị di động. Chi tiết tham             khảo ANDROID_HVPROTO .
b. Code của ứng dụng có thể debug được bằng Hierarchy Viewer. Code demo: Sunshine app

Sử dụng Hierarchy Viewer:

  • Kết nối thiết bị vào máy tính.
  • Mở source code ứng dụng trong Android Studio, build và chạy trên thiết bị.
  • Từ Android Studio, chạy Android Device Monitor: Tools > Android > Android Device Monitor.
  • Cho phép USB Debugging (nếu bị hỏi).
  • Hình dưới đây mô tả màn hình khởi tạo của Hierarchy Viewer (có thể khác nhau tùy vào setting mỗi máy).
    Hãy đảm bảo thiết bị và package ứng dụng được hiển thị ở mục Devices (DDMS mode) or Windows (Hierarchy Viewer mode) tab. Có thể chọn Window > Reset Perspective để quay về cách sắp xếp mặc định.
  • Nếu ứng dụng không hiển thị, check xem bạn đã làm đúng theo device setup instructions, sau đó quay lại từ         bước 1.

  • Trong Android Device Monitor (ADM), trên thanh menu, chọn Window > Open Perspective, sau đó click Hierarchy View ở popup.
    HOẶC Click nút Hierarchy
    View nếu nó đã được hiển thị.
  • Giao diện Hierarchy Viewer hiển thị. Nếu giao diện hiển thị không giống thì có thể chọn Window > Reset Perspective.
  • Click đúp vào ứng dụng ở trong tab Windows. Ở đây sẽ mô tả cấu trúc tĩnh của toàn bộ view trong ứng dụng.
  • Click Layout View tab để ẩn Console và hiển thị wireframe, của các view đang được chọn sẽ có outline màu đỏ. Có thể click outline để chọn. Các thành phần còn lại:a. Tree Overview góc trên bên phải cho ta cái nhìn toàn cảnh về ViewHierarchy
    b. Trong Tree Overview, di chuyển hình chữ nhật xám để chuyển phần cấu trúc hiển thị ở Tree View (ở giữa)
    c. Trong Tree View, có thể kéo và zoom bằng chuột.Chi tiết các phần refer Debugging UI tools mục About the View Hierarchy window.
  • Click vào một View node để hiển thị chi tiết. Đây là ví dụ nhìn gần của một node và chi tiết của nó.
  • Click tab View Properties ở góc trên (cạnh tab Windows) để xem các thuộc tính của Node đã chọn.

  • Click đúp một View node trong Tree View để render nó trong popup window.

  • Nodes trong Tree View hiển thị kiểu (class), và id trong source code.

  • Debug: (redraw bằng cách invalidate rồi request layout cho 1 view)
    a. Chọn một View trong Tree View. Việc redraw một đối tượng high-level (gần về phía root) thường kéo theo việc redraw các đối tượng lower-level.
    b. Click Invalidate ở trên của window. Việc này sẽ đánh dấu View là invalid, và sắp xếp nó vào lịch redraw ở lần request layout tiếp theo.
    c. Click Request Layout để request layout. View đã chọn và các con của nó được redraw, cũng như các View khác cần được redraw.Tự redraw một View cho phép ta theo dõi View object tree và kiểm tra thuộc tính của từng View từng bước một giống như đi qua từng break point ở trong source code.
  • View Hierarchy còn giúp ta xác định việc render chậm. Bắt đầu bằng việc nhìn vào View nodes với indicator đỏ hoặc vàng để xác định các View bị render chậm. Khi ta chạy qua hết ứng dụng, ta có thể đánh giá xem View đó render chậm trong cả ứng dụng hay chỉ trong một vài trường hợp.Nên nhớ rằng việc render chậm không nhất thiết là dấu hiệu cho một vấn đề, nhất là với ViewGroup. View có nhiều con và các View phức tạp sẽ render chậm hơn.View Hierarchy window cũng giúp ta tìm ra các vấn đề performance. Chi cần hình vào các hình tròn indicator màu vàng/đỏ ở mỗi View, ta có thể thấy View nào measure, layout, và draw chậm nhất. Từ đó, ta có thể nhanh chóng xác định vấn đề cần giải quyết trước.

 

Ví dụ xác định và tối ưu layout bằng Hierarchy Viewer

  1. Đây là View Hierarchy của item phía trên.

    Dễ thấy đây là một cấu trúc với 3 tầng và có vài vấn đề với text items (hình trái). Click vào items hiển thị thời gian tiêu tốn cho mỗi giai đoạn xử lý (hình phải) . Ta có thể thấy rõ item nào cần nhiều thời gian nhất để measure, layout, và render, đồng thời cũng là đối tượng cần tối ưu. Theo hình, thời gian cần để hoàn thành render item là:
    Measure: 0.977ms
    Layout: 0.167ms
    Draw: 2.717ms
  2. Do layout performance bên trên bị chậm lại bởi nestedLinearLayout, performance có thể được cải thiện bằng cách làm phẳng (flattening) layout – làm layout nông và rộng, hơn là hẹp và sâu. Sử dụng RelativeLayout như  root node sẽ cải thiện vấn đề này, khi chuyển sang và lặp lại các bước trên ta sẽ có kết quả như sau:

    Giờ việc render mỗi item tốn:

    • Measure: 0.598ms
    • Layout: 0.110ms
    • Draw: 2.146ms

    Có thể chỉ là sự cải thiện nhỏ so với trước đó, tuy nhiên thời gian này sẽ nhân lên nhiều lần (bằng với số item trong list).

    Hầu hết sự khác biệt thời gian ở đây là do sử dụng thuộc tính layout_weight của LinearLayout, khiến tốc độ measurement bị chậm đi (phải measure lại). Đây chỉ là một ví dụ đơn giản về việc sử dụng layout bất hợp lý và ta nên cẩn trọng trong việc có nên sử dụng layout weight hay không.


 

 

Lời kết:

Vậy là chúng ta đã kết thúc phần tìm hiểu sơ lược liên quan đến tối ưu hóa giao diện Android. Mọi thắc mắc, góp ý xin vui lòng bình luận xuống bên dưới. Hi vọng chúng ta có thể cùng tìm hiểu sâu hơn về chủ đề này qua các bài viết tiếp theo trong tương lai không xa.

 


Bài viết có sử dụng kiến thức tham khảo từ các trang sau:

  • https://medium.com/google-developers/exceed-the-android-speed-limit-b73a0692abc1#.6ksitn6ey
  • http://developer.android.com/tools/performance/hierarchy-viewer/index.html#WhatYouNeed
  • http://developer.android.com/tools/performance/hierarchy-viewer/setup.html
  • http://developer.android.com/training/improving-layouts/optimizing-layout.html

Một số link tham khảo thêm:

  • http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html
  • http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-with.html
  • https://www.youtube.com/watch?v=we6poP0kw6E&list=UU_x5XG1OV2P6uZZ5FSM9Ttw&annotation_id=annotation_107155567&feature=iv&src_vid=CaMTIgxCSqU
  • https://opensignal.com/blog/2013/07/30/40-developer-tips-for-android-optimization/


Instruments – Bộ công cụ tiện ích trong Xcode