Tối ưu hoá UI của ứng dụng có thực sự cần thiết - Android Performance [Part2]

Chúng ta đang cùng nhau tối ưu hoá hiệu năng ứng dụng Android ( Android Performance ), chắc hẳn qua bài viết trước Part1 ít nhiều các bạn đã rút ra được một số lưu ý quan trọng khi phát triển ứng dụng rồi phải không nào ? Mong muốn không chỉ dừng ở đó mà cần phải làm tốt hơn nữa, mượt thêm một chút nữa và mức độ trải nghiệm ứng dụng của chúng ta không làm USER cảm thấy "tắc"... Vì vậy mình tiếp tục cập nhật thêm phần 2 này, bắt đầu thôi nào 😄

1. Tối ưu cấp độ của giao diện

Trước giờ khi nói đến việc Optimize có thể mình sẽ phải thực hiện một vài công việc liên quan đến code Java mà ít chú ý đến vấn đề nằm ở cả XML - chính là UI . Điều này rất hay gặp lắm đó các bạn, đặc biệt với những màn hình có độ phức tạp cao lại càng dễ xảy ra. Một ví dụ khi cần tạo 1 layout đơn giản như sau :

Và khi xử lý trong file XML thì có cấu trúc tương tự như này :

Sau khi phân tích cấp độ (Hierarchy) của layout trên chúng ta có được 1 sơ đồ phân cấp từ việc bóc tách từng Object layout đã được dùng và với mỗi lớp sẽ là 1 cấp :

Bởi vì ở trên có LinearLayout nồng nhau nên làm cho performance kém đi. => Điều mong muốn tốt hơn : Có cách nào giảm bớt cấp độ trên không ? Và rõ ràng trước mắt chúng ta sẽ có 1 cấu trúc layout đơn giản hơn, nhưng performance được cải thiện hơn. Giải pháp: Hãy thử chuyển qua dùng RelativeLayout . Bây giờ chúng ta có 1 chút thay đổi trong cấu trúc rồi, do RelativeLayout mềm dẻo hơn LinearLayout Bây giờ đã có 1 sự chuyển biến từ layout 3 cấp về 2 cấp, nhưng có 1 sự khác biệt là không có Layout Parent nào bị nồng vào nhau nữa. Một vài lời khuyến cáo của Google như sau :

Useless parent - A layout with children that has no siblings, is not a ScrollView or a root layout, and does not have a background, can be removed and have its children moved directly into the parent for a flatter and more efficient layout hierarchy. Deep layouts - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as RelativeLayout or GridLayout to improve performance. The default maximum depth is 10.

Lưu ý: Chỉ nên tạo ra 1 Layout có cấp độ cao nhất là 10

Mở rộng: Nhiều lúc chúng ta hay dùng attribute layout_weight với LinearLayout, trên thực tế nó chỉ là cách tạo bố cục của LinearLayout và nó làm chậm performance UI của bạn. Nên dùng trong trường hợp thực sự cần thiết thôi.

2. Dùng lại Layout giống nhau

Thực tế các Layout giống nhau thường xuyên diễn ra, ví dụ : Toolbar, Footer, Avatar Info, WebView Layout,... Khi muốn dùng lại bất kì 1 layout nào, bạn chỉ việc tạo riêng ra 1 file .xml sau đó sử dụng thẻ <include/>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_bg"
    android:gravity="center_horizontal">

    <include layout="@layout/titlebar"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="@string/hello"
              android:padding="10dp" />

    ...

</LinearLayout>

Mở rộng: Sử dụng thẻ <merge /> Trong trường hợp bạn có 1 layout dùng chung là footer.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_bg"
    android:gravity="center_horizontal">
    
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/add"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/delete"/>
 </LinearLayout>

Lúc này bạn sử thẻ <include/> cho bất kì layout nào sẽ vô hình chung tăng thêm 1 cấp độ nữa. Và hạn chế điều này bằng cách chỉnh sửa lại layout footer.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/add"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/delete"/>

</merge>

Dù cho bây giờ bạn dùng thẻ include thì hệ thống sẽ bỏ qua thẻ merge vào toàn bộ nội dung trong thẻ merge sẽ nằm trong layout mà bạn đã include. Các bạn hãy thử code nếu chưa làm vậy bao giờ nhé 😉

3. Tạo độ trễ khi đang Load View

Những lúc đang connect tới Server site để lấy Data về và hiển thị lên màn hình , do nguyên nhân đường truyền chậm , data tải về tương đối lớn... View không thể hiển thị kịp và USER cảm giác bị giật. Có một giải pháp khá hay là sử dụng ViewStub, cách dùng rất dễ dàng. ViewStub sẽ tạo ra một lớp mờ trên màn hình trước khi toàn bộ view được load xong, khi mọi thứ đã sẵn sàng để "show" bạn chỉ cần set visible như những view bình thường khác Đây là 1 đoạn code ví dụ:

<ViewStub
    android:id="@+id/stub_import"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/progress_overlay"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom" />

Thao tác để hiển thị như sau :

findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);

Lưu ý: Hiện nay vẫn chưa support sử dụng thẻ <merge>

4. Tổng kết

Vừa rồi mình đã cập nhật thêm những cách tối ưu hiệu năng về UI trong ứng dụng Android, hy vọng với những cách làm ở trên sẽ giúp ích cho các bạn thật nhiều và bớt công sức phải tìm tòi ở nhiều nguồn khác nhau trong bài toán tối ưu hoá hiệu năng Android. Mình welcome những bổ xung và kinh nghiệm xử lý của các bạn bằng việc để lại comment phía dưới nhé !!! Không gì bằng bắt tay vào triển khai thử thôi các bạn nhỉ ? 😄 Happy coding !