Giảm kích thước ứng dụng

Người dùng thường tránh tải những ứng dụng có vẻ có kích thước quá lớn, đặc biệt là ở các thị trường mới nổi, nơi thiết bị kết nối với mạng 2G và 3G không ổn định hoặc dùng các gói có hạn mức dữ liệu. Trang này mô tả cách giảm kích thước tải xuống của ứng dụng, giúp nhiều người dùng tải ứng dụng xuống hơn.

Tải ứng dụng lên bằng Android App Bundle

Tải ứng dụng lên dưới dạng tệp Android App Bundle để lưu ngay kích thước ứng dụng khi bạn xuất bản lên Google Play. Android App Bundle là một định dạng tải lên bao gồm tất cả tài nguyên và mã đã biên dịch của ứng dụng nhưng trì hoãn việc tạo tệp APK và đăng nhập vào Google Play.

Sau đó, mô hình phân phát ứng dụng của Google Play sẽ dùng gói ứng dụng của bạn để tạo và phân phát các tệp APK được tối ưu hoá cho cấu hình thiết bị của từng người dùng để họ chỉ tải mã và tài nguyên cần thiết xuống nhằm chạy ứng dụng của bạn. Bạn không cần tạo, ký và quản lý nhiều tệp APK để hỗ trợ nhiều thiết bị, còn người dùng sẽ có thể tải xuống các tệp có kích thước nhỏ hơn và được tối ưu hoá hơn.

Vì Google Play hạn chế kích thước tệp nén tải xuống là từ 200 MB trở xuống cho các ứng dụng được xuất bản bằng gói ứng dụng, do đó bạn vẫn nên áp dụng các nguyên tắc được mô tả trên trang này để giảm kích thước tải xuống của ứng dụng nhiều nhất có thể.

Tìm hiểu cấu trúc tệp APK

Trước khi giảm kích thước ứng dụng, bạn nên tìm hiểu cấu trúc tệp APK của ứng dụng. Tệp APK bao gồm một tệp lưu trữ ZIP chứa tất cả các tệp tạo nên ứng dụng. Những tệp này bao gồm tệp lớp Java, tệp tài nguyên và một tệp chứa tài nguyên đã biên dịch.

Tệp APK có các thư mục sau:

  • META-INF/: chứa các tệp chữ ký CERT.SFCERT.RSA cũng như tệp kê khai MANIFEST.MF.
  • assets/: chứa các tài sản của ứng dụng. Ứng dụng có thể truy xuất các tài sản này bằng cách sử dụng một đối tượng AssetManager.
  • res/: chứa các tài nguyên không được biên dịch vào resources.arsc.
  • lib/: chứa mã được biên dịch dành riêng cho lớp phần mềm của bộ xử lý. Thư mục này chứa một thư mục con cho từng loại nền tảng, chẳng hạn như armeabi, armeabi-v7a, arm64-v8a, x86, x86_64mips.

Tệp APK cũng chứa các tệp sau. Chỉ có AndroidManifest.xml là bắt buộc:

  • resources.arsc: chứa những tài nguyên đã biên dịch. Tệp này chứa nội dung XML từ mọi cấu hình của thư mục res/values/. Công cụ đóng gói trích xuất nội dung XML này, biên dịch nội dung thành dạng nhị phân và lưu trữ nội dung đó. Nội dung này bao gồm các chuỗi và kiểu ngôn ngữ, cũng như đường dẫn đến nội dung không được đưa trực tiếp vào tệp resources.arsc, chẳng hạn như tệp bố cục và hình ảnh.
  • classes.dex: chứa các lớp được biên dịch ở định dạng tệp DEX bằng máy ảo Dalvik hoặc ART.
  • AndroidManifest.xml: chứa tệp kê khai chính của Android. Tệp này liệt kê tên, phiên bản, quyền truy cập và những tệp được tham chiếu trong thư viện của ứng dụng. Đồng thời, tệp này sử dụng định dạng XML nhị phân của Android.

Giảm số lượng và kích thước tài nguyên

Kích thước của tệp APK có tác động đến tốc độ tải của ứng dụng, dung lượng bộ nhớ sử dụng và mức tiêu thụ năng lượng của ứng dụng. Bạn có thể thu nhỏ tệp APK bằng cách giảm số lượng và dung lượng tài nguyên chứa trong tệp APK đó. Cụ thể là bạn có thể xoá những tài nguyên mà ứng dụng không còn sử dụng, ngoài ra bạn còn có thể dùng các đối tượng Drawable có thể mở rộng thay cho tệp hình ảnh. Phần này thảo luận về các phương pháp nói trên và một số cách khác để bạn có thể giảm tài nguyên trong ứng dụng nhằm giảm kích thước tổng thể của tệp APK.

Xoá tài nguyên không bạn không dùng

Công cụ lint là một công cụ phân tích mã tĩnh trong Android Studio, giúp phát hiện những tài nguyên trong thư mục res/ mà mã nguồn của bạn không tham chiếu đến. Khi phát hiện thấy một tài nguyên dự án của bạn có thể không dùng đến, công cụ lint sẽ hiển thị một thông báo tương tự như sau:

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
    to be unused [UnusedResources]

Các thư viện mà bạn thêm vào mã nguồn có thể chứa tài nguyên không dùng đến. Gradle có thể thay mặt bạn tự động xoá các tài nguyên nếu bạn bật shrinkResources trong tệp build.gradle.kts của ứng dụng.

Kotlin

android {
    // Other settings.

    buildTypes {
        getByName("release") {
            minifyEnabled = true
            shrinkResources = true
            proguardFiles(getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro")
        }
    }
}

Groovy

android {
    // Other settings.

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

Để sử dụng shrinkResources, hãy bật tính năng rút gọn mã. Trong quá trình dựng, trước tiên, R8 sẽ xoá đoạn mã không dùng đến. Sau đó, trình bổ trợ Android cho Gradle sẽ xoá những tài nguyên không dùng đến.

Để biết thêm thông tin về việc rút gọn mã nguồn và tài nguyên, cũng như các cách Android Studio giúp giảm kích thước tệp APK, vui lòng xem phần Rút gọn, làm rối mã nguồn và tối ưu hoá ứng dụng.

Trong trình bổ trợ Android cho Gradle 7.0 trở lên, bạn có thể khai báo những cấu hình mà ứng dụng hỗ trợ. Gradle truyền thông tin này đến hệ thống xây dựng bằng cách dùng phiên bản resourceConfigurations và tuỳ chọn defaultConfig. Sau đó, hệ thống xây dựng sẽ ngăn các tài nguyên của các cấu hình khác (không được hỗ trợ) xuất hiện trong tệp APK, làm giảm kích thước của tệp APK. Để biết thêm thông tin về tính năng này, vui lòng xem phần Xoá tài nguyên thay thế không dùng đến.

Giảm thiểu việc sử dụng tài nguyên trong thư viện

Khi phát triển một ứng dụng Android, bạn thường dùng các thư viện bên ngoài để cải thiện khả năng hữu dụng và tính linh hoạt của ứng dụng. Ví dụ: bạn có thể tham chiếu đến AndroidX để cải thiện trải nghiệm người dùng trên các thiết bị cũ, hoặc bạn có thể sử dụng Dịch vụ Google Play để truy xuất bản dịch tự động cho văn bản trong ứng dụng của bạn.

Nếu một thư viện được thiết kế cho máy chủ hoặc máy tính, thì có thể thư viện đó có chứa nhiều đối tượng và phương thức mà ứng dụng của bạn không cần đến. Để chỉ đưa vào những phần của thư viện mà ứng dụng cần, bạn có thể chỉnh sửa tệp của thư viện nếu giấy phép cho bạn sửa đổi thư viện đó. Bạn cũng có thể dùng một thư viện thay thế và phù hợp với thiết bị di động để thêm chức năng cụ thể vào ứng dụng của mình.

Giải mã hình ảnh động gốc

Trong Android 12 (API cấp 31), API ImageDecoder của NDK được mở rộng để giải mã tất cả dữ liệu khung và thời gian từ những hình ảnh sử dụng định dạng tệp GIF động và WebP động.

Hãy sử dụng ImageDecoder thay vì thư viện của bên thứ ba để giảm kích thước tệp APK và hưởng lợi nhờ các bản cập nhật sau này liên quan đến tính bảo mật và hiệu suất.

Để biết thêm thông tin chi tiết về API ImageDecoder, vui lòng tham khảo API referencemẫu trên GitHub.

Chỉ hỗ trợ mật độ cụ thể

Android hỗ trợ nhiều màn hình với mật độ điểm ảnh khác nhau, chẳng hạn như:

  • ldpi
  • mdpi
  • tvdpi
  • hdpi
  • xhdpi
  • xxhdpi
  • xxxhdpi

Mặc dù Android hỗ trợ các mật độ trước đó, nhưng bạn không cần xuất những thành phần đã tạo điểm ảnh sang từng mật độ.

Nếu bạn biết là chỉ một tỷ lệ nhỏ người dùng sở hữu các thiết bị có mật độ cụ thể, hãy cân nhắc xem bạn có cần nhóm các mật độ đó vào ứng dụng hay không. Nếu bạn không thêm tài nguyên cho một mật độ màn hình cụ thể, thì Android sẽ tự động thu nhỏ/phóng to dựa trên tài nguyên hiện dùng cho các mật độ màn hình khác.

Nếu ứng dụng của bạn chỉ cần phóng to/thu nhỏ hình ảnh, thì bạn có thể tiết kiệm nhiều dung lượng hơn nữa bằng cách sử dụng một biến thể duy nhất của hình ảnh trong drawable-nodpi/. Bạn nên đưa ít nhất một biến thể hình ảnh xxhdpi vào ứng dụng của mình.

Để biết thêm thông tin về mật độ của màn hình, vui lòng xem phần Kích thước và mật độ màn hình.

Dùng đối tượng có thể vẽ

Một số hình ảnh không đòi hỏi tài nguyên hình ảnh tĩnh. Thay vào đó, khung có thể tự động vẽ hình ảnh trong thời gian chạy. Đối tượng Drawable (<shape> trong XML) có thể chiếm một phần nhỏ dung lượng trong tệp APK của bạn. Ngoài ra, các đối tượng Drawable XML sẽ tạo ra hình ảnh đơn sắc tuân thủ nguyên tắc của Material Design.

Dùng lại tài nguyên

Bạn có thể đính kèm tài nguyên riêng cho các biến thể của hình ảnh, chẳng hạn như các phiên bản phủ màu, tô bóng hoặc xoay nghiêng của cùng hình ảnh. Tuy nhiên, bạn nên sử dụng lại cùng một nhóm tài nguyên và tuỳ chỉnh các tài nguyên đó khi cần thiết trong thời gian chạy.

Android cung cấp một số tiện ích để thay đổi màu của tài sản bằng cách dùng các thuộc tính android:tinttintMode.

Bạn cũng có thể bỏ qua các tài nguyên chỉ đơn giản là một tài nguyên khác được xoay nghiêng. Đoạn mã sau đây đưa ra một ví dụ về cách chuyển đổi một "ngón tay cái chỉ lên" (biểu tượng thích) thành "ngón tay cái chỉ xuống" (biểu tượng không thích) bằng cách chuyển hướng ở giữa hình ảnh và xoay ảnh 180 độ:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_thumb_up"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="180" />

Kết xuất từ mã

Bạn cũng có thể giảm kích thước tệp APK bằng cách kết xuất hình ảnh theo quy trình. Tính năng hiển thị theo quy trình giúp giải phóng dung lượng vì bạn không còn lưu trữ tệp hình ảnh trong tệp APK nữa.

Thu gọn tệp PNG

Công cụ aapt có thể tối ưu hoá tài nguyên hình ảnh đặt trong res/drawable/ bằng tính năng nén không mất dữ liệu trong quá trình dựng. Ví dụ: công cụ aapt có thể chuyển đổi tệp PNG màu thực không đòi hỏi nhiều hơn 256 màu thành tệp PNG 8 bit có bảng màu. Nếu bạn làm như vậy, hình ảnh sẽ có chất lượng tương đương, nhưng mức sử dụng bộ nhớ sẽ giảm đi.

aapt có các giới hạn sau:

  • Công cụ aapt không thu gọn tệp PNG có trong thư mục asset/.
  • Các tệp hình ảnh chỉ được dùng từ 256 màu trở xuống để công cụ aapt có thể tối ưu hoá các màu đó.
  • Công cụ aapt có thể làm tăng số tệp PNG đã nén. Để ngăn điều này, bạn có thể dùng cờ isCrunchPngs để tắt quy trình này đối với các tệp PNG:
  • Kotlin

        buildTypes.all { isCrunchPngs = false }
        

    Groovy

        buildTypes.all { isCrunchPngs = false }
        

Nén tệp PNG và JPEG

Bạn có thể giảm kích thước tệp PNG mà không làm giảm chất lượng hình ảnh bằng cách sử dụng các công cụ như pngcrush, pngquant hoặc zopflipng. Tất cả công cụ này đều giúp giảm kích thước tệp PNG nhưng vẫn giữ nguyên chất lượng ảnh theo cảm nhận.

Công cụ pngcrush đặc biệt hiệu quả. Công cụ này dùng lại các bộ lọc PNG và tham số zlib (Deflate), dựa trên từng tổ hợp bộ lọc và tham số để nén hình ảnh. Sau đó, phương thức này chọn cấu hình mang lại kết quả nén nhỏ nhất.

Để nén các tệp JPEG, bạn có thể dùng các công cụ như packJPGguetzli.

Dùng định dạng tệp WebP

Thay vì dùng tệp PNG hoặc JPEG, bạn cũng có thể dùng định dạng tệp WebP cho hình ảnh của mình. Định dạng WebP cung cấp tính năng nén có tổn hao và độ trong suốt (như JPG và PNG), đồng thời có thể có khả năng nén tốt hơn so với JPEG hoặc PNG.

Bạn có thể chuyển đổi hình ảnh BMP, JPG, PNG hoặc GIF tĩnh hiện có sang định dạng WebP bằng Android Studio. Để biết thêm thông tin, hãy xem bài viết Tạo hình ảnh WebP.

Dùng đồ hoạ vectơ

Bạn có thể dùng đồ hoạ vectơ để tạo biểu tượng độc lập về độ phân giải và những nội dung đa phương tiện có thể mở rộng khác. Bạn có thể dùng những yếu tố đồ hoạ này để giảm đáng kể kích thước tệp APK. Ảnh vectơ được biểu thị trong Android ở dạng đối tượng VectorDrawable. Với đối tượng VectorDrawable, tệp có kích thước 100 byte có thể tạo ra một hình ảnh sắc nét bằng kích thước màn hình.

Tuy nhiên, hệ thống cần nhiều thời gian hơn đáng kể để kết xuất từng đối tượng VectorDrawable, và các hình ảnh lớn hơn sẽ mất nhiều thời gian hơn để xuất hiện trên màn hình. Do đó, hãy cân nhắc chỉ sử dụng những đồ hoạ vectơ này khi cho thấy hình ảnh nhỏ.

Để biết thêm thông tin về cách xử lý các đối tượng VectorDrawable, hãy xem nội dung về Đối tượng có thể vẽ.

Dùng đồ hoạ vectơ cho ảnh động

Đừng dùng AnimationDrawable để tạo ảnh động theo từng khung hình, vì việc này yêu cầu bạn phải đưa vào một tệp bitmap riêng cho từng khung ảnh động, làm tăng đáng kể kích thước tệp APK của bạn.

Thay vào đó, hãy dùng AnimatedVectorDrawableCompat để tạo vectơ động có thể vẽ.

Giảm mã gốc và mã Java

Bạn có thể sử dụng các phương thức sau để giảm kích thước của mã Java và cơ sở mã gốc trong ứng dụng của mình.

Xoá mã được tạo không cần thiết

Hãy nhớ tìm hiểu dấu vết của mã được tạo tự động. Chẳng hạn như nhiều công cụ vùng đệm giao thức tạo ra quá nhiều phương thức và lớp, điều này có thể làm tăng gấp đôi hoặc gấp ba kích thước của ứng dụng.

Tránh giá trị enum

Một giá trị enum có thể thêm kích thước khoảng 1,0 đến 1,4 KB vào tệp classes.dex của ứng dụng. Những nội dung bổ sung này có thể nhanh chóng tích luỹ cho các hệ thống phức tạp hoặc thư viện dùng chung. Nếu có thể, hãy cân nhắc việc sử dụng chú thích @IntDefrút gọn mã để loại bỏ giá trị enum và chuyển đổi chúng thành số nguyên. Loại hình chuyển đổi này giữ được tất cả lợi ích về an toàn về kiểu cho enum.

Giảm kích thước của tệp nhị phân gốc

Nếu ứng dụng của bạn sử dụng mã gốc và Android NDK, bạn cũng có thể giảm kích thước của phiên bản phát hành ứng dụng bằng cách tối ưu hoá mã nguồn. Có 2 kỹ thuật hữu ích là xoá các biểu tượng gỡ lỗi và không trích xuất các thư viện gốc.

Xoá biểu tượng gỡ lỗi

Việc sử dụng biểu tượng gỡ lỗi sẽ hợp lý nếu ứng dụng của bạn đang trong quá trình phát triển và vẫn cần được gỡ lỗi. Hãy dùng công cụ arm-eabi-strip (được cung cấp trong Android NDK) để xoá các biểu tượng gỡ lỗi không cần thiết khỏi các thư viện gốc. Sau đó, bạn có thể biên soạn bản dựng phát hành của mình.

Tránh trích xuất các thư viện gốc

Khi tạo phiên bản phát hành của ứng dụng, hãy đóng gói tệp .so không nén trong tệp APK bằng cách đặt useLegacyPackaging thành false trong tệp build.gradle.kts của ứng dụng. Việc vô hiệu hoá cờ này sẽ ngăn PackageManager sao chép tệp .so từ tệp APK sang hệ thống tệp trong quá trình cài đặt. Phương thức này làm cho các bản cập nhật ứng dụng của bạn nhỏ hơn.

Duy trì nhiều tệp APK tinh gọn

Có thể tệp APK của bạn có chứa nội dung mà người dùng tải xuống nhưng không bao giờ sử dụng, chẳng hạn như tài nguyên ngôn ngữ bổ sung hoặc mật độ theo màn hình. Để giúp đảm bảo người dùng có thể tải tệp có kích thước nhỏ nhất, hãy tải ứng dụng của bạn lên Google Play bằng Android App Bundle. Việc tải gói ứng dụng lên cho phép Google Play tạo và phân phát các tệp APK đã tối ưu hoá cho cấu hình thiết bị của từng người dùng, nhờ đó, họ chỉ tải mã nguồn và tài nguyên cần thiết để chạy ứng dụng của bạn. Bạn không cần tạo, ký và quản lý nhiều tệp APK để hỗ trợ nhiều loại thiết bị và người dùng sẽ nhận được kích thước tải xuống nhỏ hơn cũng như được tối ưu hoá tốt hơn.

Nếu không xuất bản ứng dụng lên Google Play, thì bạn có thể phân đoạn ứng dụng của mình thành nhiều tệp APK, phân biệt theo các yếu tố chẳng hạn như kích thước màn hình hoặc khả năng hỗ trợ hoạ tiết của GPU.

Khi người dùng tải ứng dụng xuống, thiết bị của họ sẽ nhận được tệp APK chính xác dựa trên các tính năng và chế độ cài đặt của thiết bị. Theo đó, thiết bị không phải nhận tài sản cho những tính năng mà thiết bị không có. Ví dụ như nếu người dùng có thiết bị hdpi, thì họ không cần tài nguyên xxxhdpi mà bạn có thể đưa vào cho các thiết bị có màn hình với mật độ hiển thị cao hơn.

Để biết thêm thông tin, hãy xem phần Tạo nhiều tệp APKHỗ trợ nhiều tệp APK.