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

Người dùng thường tránh tải các ứng dụng có vẻ quá lớn, đặc biệt là tại các thị trường mới nổi, nơi thiết bị kết nối với các mạng 2G và 3G thường xuyên bị hỏng hoặc hoạt động theo gói trả tiền theo byte. 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 Bundles

Cách dễ nhất để giảm kích thước ứng dụng ngay khi phát hành lên Google Play là tải ứng dụng lên dưới dạng Android App Bundle. Đây là một định dạng tải lên mới bao gồm tất cả mã và tài nguyên đã được biên dịch của ứng dụng, nhưng trì hoãn việc tạo APK và ký vào Google Play.

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

Vui lòng lưu ý vì Google Play áp dụng hạn chế về kích thước tệp nén tải xuống với kích thước 150 MB trở xuống đối với các ứng dụng được phát hành bằng gói ứng dụng, bạn vẫn nên áp dụng các hướng dẫn đượ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ể.

Đối với các ứng dụng mà bạn phát hành lên Google Play bằng cách tải APK đã ký lên, kích thước tệp nén tải xuống sẽ bị giới hạn ở mức 100 MB trở xuống.

Tìm hiểu cấu trúc APK

Trước khi thảo luận về cách giảm kích thước ứng dụng, bạn nên tìm hiểu cấu trúc 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 thành phần của ứng dụng. Ứng dụng có thể truy xuất thành phầ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 tổng hợp 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.

Một APK cũng chứa các tệp sau. Trong số đó, chỉ có AndroidManifest.xml là bắt buộc.

  • resources.arsc: Chứa các 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.

    Lưu ý: Không nén tệp này trong APK của bạn.

  • 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/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. Tệp 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 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ụ của ứng dụng. Một trong những cách đơn giản để thu nhỏ APK là giảm số lượng và dung lượng tài nguyên chứa trong 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 vừa đề cập và một số cách khác để bạn có thể giảm tài nguyên trong ứng dụng của mình nhằm giảm kích thước tổng thể của APK.

Xoá tài nguyên không sử dụng

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

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

Lưu ý: Công cụ lint không quét thư mục assets/, thành phần được tham chiếu qua tệp phản chiếu, hoặc tệp thư viện mà bạn đã liên kết với ứng dụng. Ngoài ra, công cụ này sẽ không xoá tài nguyên mà chỉ thông báo cho bạn biết về việc có tài nguyên.

Các thư viện mà bạn thêm vào mã có thể chứa tài nguyên không sử dụng. 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 của ứng dụng.

Groovy

android {
    // Other settings

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

Kotlin

android {
    // Other settings

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

Để sử dụng shrinkResources, bạn cũng phải bật tính năng thu nhỏ mã. Trong quá trình dựng, trước tiên, R8 sẽ xóa mã không sử dụng. Sau đó, trình bổ trợ Gradle dành cho Android sẽ xóa những tài nguyên không dùng đến.

Để biết thêm thông tin về việc thu nhỏ mã và tài nguyên, cũng như các cách khác mà Android Studio giúp bạn giảm kích thước 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 0.7 trở lên, bạn có thể khai báo những cấu hình mà ứng dụng hỗ trợ. Gradle chuyển thông tin này đến hệ thống xây dựng bằng cách sử dụng phiên bản resConfigresConfigs cùng với 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 APK, làm giảm kích thước của 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 sử dụng.

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 Thư viện hỗ trợ Android để 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ể 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.

Nếu một thư viện được thiết kế cho máy chủ hoặc máy tính, thì thư viện đó có thể chứa nhiều đối tượng và phương thức mà ứng dụng của bạn không cầ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ế, thân thiện với thiết bị di động để thêm chức năng cụ thể vào ứng dụng của mình.

Lưu ý: việc rút gọn mã có thể dọn dẹp một số mã không cần thiết của thư viện, nhưng không thể xoá một số phần phụ thuộc nội bộ lớn.

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

Trong Android 12 (API cấp 31), API NDK
ImageDecoder đã đượ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
GIF động và tệp
WebP động. Khi được giới thiệu trong Android 11, API này chỉ giải mã hình ảnh đầu tiên từ ảnh động ở các định dạng này.

Sử dụng ImageDecoder thay vì thư viện của bên thứ ba để giảm kích thước APK và hưởng lợi từ các bản cập nhật trong tương lai 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, vui lòng tham khảo
API reference
mẫu trên kho lưu trữ GitHub.

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

Android hỗ trợ một tập hợp thiết bị rất lớn, bao gồm nhiều mật độ màn hình. Trong Android 4.4 (API cấp 19) trở lên, khung này hỗ trợ nhiều mật độ: ldpi, mdpi,
tvdpi, hdpi, xhdpi,
xxhdpixxxhdpi. Mặc dù Android hỗ trợ tất cả những mật độ này, nhưng bạn không cần xuất các 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àn hình cụ thể, thì Android sẽ tự động mở rộng tài nguyên hiện có cho các mật độ màn hình khác.

Nếu ứng dụng của bạn chỉ cần thu nhỏ hình ảnh, thì bạn có thể tiết kiệm nhiều không gian 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/. Mỗi ứng dụng nên có ít nhất một biến thể hình ảnh xxhdpi.

Để 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 các đối tượng có thể vẽ

Một số hình ảnh không yêu cầu 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ỏ không gian trong 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ủ các nguyên tắc thiết kế Material Design.

Sử dụng lại tài nguyên

Bạn có thể đưa vào một tài nguyên riêng biệt 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 trong cùng của một 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 thành phần, bằng cách sử dụng các thuộc tính android:tinttintMode trên Android 5.0 (API cấp 21) trở lên. Đối với các phiên bản thấp hơn của nền tảng, hãy sử dụng lớp ColorFilter.

Bạn cũng có thể bỏ qua các tài nguyên chỉ tương đương với một tài nguyên khác được xoay vòng. Đoạn mã sau đây cung cấp một ví dụ về cách chuyển đổi một “ngón tay cái lên” thành “ngón tay cái xuống” bằng cách xoay ở giữa hình ảnh và xoay góc 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 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 APK nữa.

Tệp PNG xử lý

Công cụ aapt có thể tối ưu hóa 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 tạo bản dựng. Ví dụ như công cụ aapt có thể chuyển đổi tệp PNG màu thực không yêu cầu nhiều hơn 256 màu thành tệp PNG 8 bit với một 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 nhau, nhưng kích thước bộ nhớ nhỏ hơn.

Vui lòng lưu ý 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 cần phải có tối thiểu 256 màu để 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:

Groovy

buildTypes.all { isCrunchPngs = false }

Kotlin

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ả những công cụ này đều có thể giảm kích thước tệp PNG trong khi vẫn duy trì chất lượng hình ảnh đã biết.

Công cụ pngcrush đặc biệt hiệu quả: Công cụ này sử 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ể sử dụng các công cụ như packJPG và guetzli.

Sử 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 khi nhắm mục tiêu Android 3.2 (API cấp 13) trở lên.
Định dạng WebP cung cấp tính năng nén có tổn hao (như JPEG) cũng như độ trong suốt (như PNG), nhưng có thể 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 chi tiết, vui lòng xem bài viết Tạo hình ảnh WebP bằng Android Studio.

Sử dụng đồ họa 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 nghe nhìn có thể mở rộng khác. Việc sử dụng những đồ hoạ này có thể làm giảm đáng kể kích thước 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 một khoảng thời gian đáng kể để hiển thị 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 hiển thị hình ảnh nhỏ.

Để biết thêm thông tin về cách làm việc với các đối tượng VectorDrawable, vui lòng xem phần Làm việc với Tài nguyên có thể vẽ.

Sử dụng đồ họa vectơ cho hình ảnh động

Không sử 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, điều này làm tăng đáng kể kích thước APK.

Thay vào đó, bạn nên sử dụng
AnimatedVectorDrawableCompat để tạo hình vẽ vectơ động.

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

Bạn có thể dùng một số phương pháp để 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 kích thước 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 liệt kê

Một 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 @IntDef và thu hẹp mã để loại bỏ giá trị liệt kê và chuyển đổi chúng thành số nguyên. Loại chuyển đổi này duy trì tất cả các lợi ích về an toàn trong loại 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ã. 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, các gói .so chưa nén được đặt trong APK bằng cách đảm bảo useLegacyPackaging được đặt thành false trong tệp build.gradle 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ừ APK sang hệ thống tệp trong quá trình cài đặt và mang lại thêm lợi ích khi giảm kích thước các bản cập nhật của ứng dụng.

Duy trì nhiều APK tinh gọn

APK của bạn có thể 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 độ trên màn hình. Để đảm bảo người dùng có thể tải xuống một cách tối thiểu, bạn nên tải ứng dụng 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 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ã và tài nguyên cần thiết để chạy ứng dụng của bạn. Bạn không còn phải tạo, ký và quản lý nhiều APK để hỗ trợ các thiết bị khác nhau 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 phát hành ứ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 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ợ kết cấu GPU.

Khi người dùng tải ứng dụng xuống, thiết bị của họ sẽ nhận được 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 thành phầ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 chi tiết, vui lòng xem phần Định cấu hình phân tách APK và Duy trì nhiều APK.