12/08/2018, 14:19

Tối ưu hiệu suất của Vector Drawables Trong Android

Trong khi một vài nền tảng mobile đã được hỗ trợ đồ hoạ vector( vector graphic) thì Android mới chỉ bắt đầu làm điều này từ API Level 21 với sự hỗ trợ của Support Library 23.2.0 cho các devices trước Lollipop. Bằng việc thay thế các source image PNG bằng VectorDrawables, kích thước các file APK của ...

Trong khi một vài nền tảng mobile đã được hỗ trợ đồ hoạ vector( vector graphic) thì Android mới chỉ bắt đầu làm điều này từ API Level 21 với sự hỗ trợ của Support Library 23.2.0 cho các devices trước Lollipop. Bằng việc thay thế các source image PNG bằng VectorDrawables, kích thước các file APK của bạn sẽ giảm đi, các images của bạn trông sẽ đẹp hơn và độc lập với độ phân giải của devices.

Hệ thống sẽ cố găng vễ lại Activity khoảng 16ms vậy nó có thể đạt được 60fps. Vậy điều này có nghĩa răng thời gian cho mỗi lần phương thức onDraw được gọi là vô cùng quan trọng.

Khi sử dụng không chính xác,VectorDrawables có thể ảnh hưởng tới hiệu suất trong app của bạn bởi vì việc vẽ có thể lấy khoảng thời gian dài. Trong bài viết này sẽ đề cập tới việc sử dụng VectorDrawables để cái thiện hiệu suất của app.

Hiểu về VectorDrawables

đồ hoạ vector(vector graphic) sử dụng những hình học để mô tả các thành phần dồ hoạ. Các đồ hoạ vector đươc vẽ tại thời điểm chạy. Hình ảnh sẽ tự động vẽ mật độ pixel sao cho hình ảnh được mượt không phụ thuộc vào khả năng của thiết bị. Vậy hình ảnh của bạn sẽ không bị downscale or upscale,hay bị kéo dài. Chúng sẽ luôn luôn được vẽ hoàn hảo cho kích thước màn hình.

0-QTW1NnO8XlucZo-Z.png

Vector drawables cho phép trình bày các image như là icon, các thành phần UI... dựa trên đồ hoạ vector xml. Số lượng và kích thước các resource cần phải thêm vào project sẽ giảm đi nghĩa là không cần phải thêm các icon, cái file png trong các thư mục tương ứng như drawable-mdpi, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi nữa. mà chỉ cần duy nhất 1 bộ icon, hay 1 bộ file png trong duy nhất 1 thư muc drwable. Do đó kích thước của file APK sẽ gỉam đi. Đây là một file vector drawable.

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:awidth="336dp"
    android:height="548dp"
    android:viewportHeight="548"
    android:viewportWidth="336">

    <path
        android:fillColor="#FFFFFF"
        android:pathData="M 4 0 L 332 0 Q 336 0 336 4 L 336 544 Q 336 548 332 548 L 4 548 Q 0 548 0 544 L 0 4 Q 0 0 4 0 Z" />

    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 280 H 312 V 292 H 16 V 280 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 304 H 267 V 316 H 16 V 304 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 328 H 307 V 340 H 16 V 328 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 354 H 213 V 366 H 16 V 354 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 207 H 316 V 227 H 16 V 207 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 16 240 H 284 V 260 H 16 V 240 Z" />
    <path
        android:fillColor="#DF6B9ED1"
        android:pathData="M 4 0 L 332 0 Q 336 0 336 4 L 336 188 Q 336 192 332 192 L 4 192 Q 0 192 0 188 L 0 4 Q 0 0 4 0 Z" />
</vector>

Mặc dù kích thước một file XML có chứa vector drawable thường có kích thước nhỏ hơn một file PNG. Nhưng vector drawable phải yêu cầu tính toán ngay trước thời điểm chạy, cái này là 1 vấn đề cho những thành phần đồ hoạ phức tạp. Khi mà vector drawable vẽ lần đầu tiên, thì một bitmap cach được tạo ra để tối ưu hiệu suất cho việc vẽ lại. Cache này sẽ được sử dụng lại miễn là chiều rộng và chiều cao của image đó cần vẽ là giống nhau. Nếu vector drawables được sử dụng lại cho nhiều size khác nhau thì môt bitmap mới sẽ được tạo ra mỗi lần. Nếu so sánh việc sử dụng cùng 1 file image png vời 1 file XML có chưa vector drawables thì tại thời điểm chạy lần đầu tiên thì việc vẽ một VectorDrawables sẽ lấy nhiều thời gian hơn. nhưng sau đó miễn là kích thước của image không thay đổi thì việc vẽ lại VectorDrawables sẽ lấy cùng số lượng thời gian khi vẽ một file png.

Nhiều Kích Thước (Multiple Sizes)

Giả sử bạn cần hiển thị một image sao cho vừa với chiều cao cảu màn hình nhưng giữ tỉ lệ giưã các cạnh khi xoay device từ portrait tới landscape. Vậy kích thước của image là khác nhau từ portrait to landscape.

0-wTEhD6n0f-RyXmYs.png

0-fZXPPdbhFdhGAcN4.png

VectorDrawables sử dụng một Bitmap cache mà đã được tạo khi kích thước thay đổi. Nghĩa là trong trường hợp này khi bạn xoay device từ portrait đến landscape. Sau lần vẽ đầu tiên, bitmap đã được cache sẽ được sử dụng. điều này có nghĩa rằng bạn sẽ dùng nhiều thời gian cho việc vẽ lần đầu tiên của VectorDrawable tại mỗi lần bạn xoay device. Nhưng nếu image mà bạn đang vẽ cùng kích thước, cả trong portrait và landscape như vậy bitmap đã được cách sẽ hợp lệ và được sử dụng lại.

Chúng ta hãy thử kiểm tra:

Test 1: Kích thước của view thay đổi thi thay đổi hướng từ 1440x2240px trong portrait tới 2560x1152px trong landscape. Khi xoay device, việc vẽ vector drawable sẽ lấy thời gian trung bình là 15,5ms trong portrait và 7.80ms trong landscapde nơi mà view có kích thước nhỏ hơn.

Test 2: Kích thước của image không đổi, luôn là 800x800px. Tôi thấy răng việc vẽ vector drawable sẽ lấy 7,50ms lần đầu tiên và thời gian vẽ lần thứ 2 giảm xuống con 0,150ms.

Test 3: Sử dụng 2 resource khác nhau cho porstrait và landscape thậm chí là cùng kích thước. Những image sẽ lấy nhiều thời gian vẽ hơn mỗi lần bạn xoay device.

Kích Thước VectorDrawable Lớn Nhât

Khi vectordrawble được vẽ tai thời điểm chạy, trên CPU băt đầu loading và drawing VectorDrawable nên sẽ bị chậm hơn. Do đó Google đề nghị việc sử dụng chúng cho các image có kích thước lơn nhất là 200 X 200 dp

0-ivBoAiEZ4KBC7-MT.png

Test 1: Tôi vẽ một image cho phù hợp với kích thước màn hình là 1440x1960px. VectorDrawable lấy thời gian trung bình để vẽ là 16,60ms, PNS là 0,180ms.

Test 2: Chúng tôi vẽ image với kích thước lớn nhất theo đề nghị của google là 200 x 200 dp hay 800 x800 px . VectorDrawable lấy 3,40ms để vẽ, con PNG lấy 0,060ms.

Khi Nào Bạn Cần Sử Dụng VectorDrawable

Thời gian vẽ của VectorDrawable bị ảnh hưởng bởi kích thước của image, độ phức tạp của nó. Nếu drawable có chứa các đường vẽ phức tạp nó sẽ lấy nhiều thời gian để vẽ hơn. Vậy trước khi bạn thêm SVG mới vào trong android studio bạn cần xem xét cái yêu tố sau:

  • Bạn có thể thay thế 1 vector đồ hoạ bằng một shape. Shape vẽ nhanh hơn VectorDrawble và file PNG.
  • Image bạn cần vẽ có đơn giản không?
  • VectorDrawble thực sự lớn, phức tap, với các đường vẽ phức tạp? Trong trường hơp này bạn nên sử dụng các file PNG thay thế, mặc dùng làm vậy thì file APK của bạn sẽ tăng nhưng hiệu suất app của bạn sẽ không bị giảm xuống.

Làm Thế Nào Bạn Test ?

Để đo hiệu suất của việc vẽ, tôi đã tạo ra 1 class MeasurableImageView được extends từ ImageView. Tôi đã override lại phương thức onDraw() để tính toán bao lâu super.onDraw() đã lấy. Tôi dùng SamSung A7 để test. Chạy app vài lần và lấy thời gian trung bình.

Sử dụng System.nanoTime() Trước và sau phương thức ImageView.onDraw, tôi có được thời gian hệ thống.Sau đó chỉ việc khấu trừ thời gian thời bắt đàu và kết thúc để có được thời gian vẽ

@Override protected void onDraw(final Canvas canvas) {
     long startTime = System.nanoTime();
     super.onDraw(canvas);
     long endTime = System.nanoTime();
     long duration = endTime - startTime;
}

Sử dụng TraceView Hi vọng có được kết quả rõ dàng hơn tôi sử dụng traceview. Tôi gọi Debug.startMethodTracing trước super.onDraw và tạm dừng với Debug.stopMethodTracing

@Override protected void onDraw(final Canvas canvas) {
     Debug.startMethodTracing("test");
     super.onDraw(canvas);
     Debug.stopMethodTracing();
}

0-VL6TBm35m6Z-UxZw.png0-t6gpO-fST5Ki9Sl2.png

Các bạn có thể download source code tại đây

0