Giảm thiểu kích cỡ ứng dụng trong Android

633

Một số cách giúp giảm kích cỡ ứng dụng Android.

1. Tại sao lại cần giảm thiểu kích cỡ trong xây dựng ứng dụng Android?

Người dùng thường tránh tải xuống các ứng dụng có vẻ quá lớn, đặc biệt là ở các thị trường mới nổi nơi các thiết bị kết nối với mạng 2G và 3G hoạt động trên các gói trả theo byte. Trong những năm gần đây kích cỡ ứng dụng và game android ngày một tăng, tính từ năm 2012 đến nay con số này đã tăng lên đến 5 lần, đây là thực sự là vấn đề đối với cả những thiết bị cao cấp với bộ nhớ lớn, các thiết bị này có thể dễ dàng bị lấp đầy bởi các nội dụng 4k chất lượng cao và bộ nhớ là một trong những nguyên nhân hàng đầu gây giảm trải nghiệm người dùng và là nguyên nhân khiến họ phải gỡ bỏ ứng dụng. Trong bài viết này cũng cấp các cách giảm kích thước APK của ứng dụng, cho phép nhiều người dùng tải xuống ứng dụng của bạn hơn.

2. Cấu trúc của một file ứng dụng (Apk).

Hãy cùng tìm hiểu cấu trúc file APK trước khi tìm cách giảm kích thước ứng dụng của bạn.

  • File APK bao gồm tệp lưu trữ ZIP chứa tất cả các tệp trong ứng dụng của bạn. Các tệp này bao gồm các tệp lớp Java, các tệp tài nguyên và một tệp chứa các file được biên dịch. Một file APK chứa các thư mục sau:
  • META-INF /: Chứa các tệp chữ ký CERT.SFCERT.RSA và tệp kê khai MANIFEST.MF.
  • assets/: Chứa tài nguyên của ứng dụng mà có thể truy xuất bằng cách sử dụng đối tượng AssetManager
  • .res/: Chứa các tài nguyên không được biên dịch thành resource.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 thư mục con cho từng loại nền tảng, như armeabi, armeabi-v7a, arm64-v8a, x86, x86_64 và mips.
  • Một file APK cũng chứa các tệp sau. Trong số các tệp đó, chỉ có AndroidManifest.xml là bắt buộc.
  • resource.arsc: Chứa các tài nguyên được biên dịch. Tệp này chứa nội dung XML từ thư mục res/value/. Công cụ đóng gói trích xuất nội dung XML này, biên dịch nó thành dạng nhị phân và lưu trữ. Nội dung này bao gồm strings và styles, cũng như các đường dẫn đến nội dung không được chứa trực tiếp trong tệp resource.arsc, chẳng hạn như layout và image.
  • class.dex: Chứa các lớp được biên dịch theo định dạng tệp DEX được hiểu bởi máy ảo Dalvik / ART.
  • AndroidManifest.xml: Là tệp kê khai của Android. Tệp này liệt kê tên, phiên bản, quyền truy cập và các tệp thư viện được tham chiếu của ứng dụng. Tệp sử dụng định dạng XML nhị phân của Android.
  10 điều mọi nhà phát triển ứng dụng Android nên biết về kiến trúc Architecture

3. Sử dụng công cụ Lint để phát hiện tài nguyên không được sử dụng.

  • Công cụ lint, một công cụ phân tích mã tĩnh có trong Android Studio, lint phát hiện các resources trong thư mục res/ của bạn mà bạn không tham chiếu đến (không sử dụng). Khi công cụ lint phát hiện ra một resources không được sử dụng trong project của bạn, nó sẽ xuất ra một thông báo như ví dụ sau.
  • Ex:

  • Lưu ý: Công cụ lint không kiểm tra thư mục assets/, assets được tham chiếu thông qua reflection hoặc tệp thư viện mà bạn đã liên kết với ứng dụng của mình. Ngoài ra, nó cũng không loại bỏ resources không sử dụng đó giúp bạn mà nó chỉ cảnh báo bạn về sự hiện của các tài nguyên không sử dụng đó thôi. Việc loại bỏ bạn sẽ phải tự thực hiện.

4. Sử dụng ShrinkResources tối ưu tài nguyên sử dụng.

  • Các thư viện mà bạn thêm vào trong ứng dụng của bạn có thể chứ một số resources không được sử dụng (thừa) và Gradle có thể tự động loại bỏ các resources thừa này nếu bạn bật chế độ shrinkResources trong file build.gradletại module app của bạn.

Sau khi bạn bật chế độ shrinkResources . Trong suốt quá trình bạn build ứng dụng, đầu tiên ProGuard sẽ loại bỏ các đoạn code không cần thiết sau đó Gradle sẽ loại bỏ các resources không cần thiết.

5. Sử dụng các thư viện ngoài một cách hợp lý.

  • Khi phát triển ứng dụng Android, bạn thường sử dụng các thư viện bên ngoài để cải thiện khả năng sử dụng và tính linh hoạt của ứng dụng. Ví dụ: bạn có thể sử dụng thư viện Android Support để cải thiện trải nghiệm người dùng trên các thiết bị cũ hơn 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 mình. Nếu một thư viện được thiết kế cho máy chủ hoặc máy tính để bàn, nó có thể bao gồm nhiều đối tượng và phương thức mà ứng dụng của bạn không cần đến. Để đảm bảo rằng ứng dụng của bạn chỉ bao gồm các phần của thư viện mà ứng dụng của bạn cần, bạn có thể chỉnh sửa các tệp của thư viện nếu giấy phép cho phép bạn sửa đổi thư viện. Bạn cũng có thể sử dụng thư viện thay thế, tối ưu cho thiết bị di động để thêm chức năng cụ thể vào ứng dụng của mình mà không làm cho ứng dụng bị phình to ra bởi các resource không cần thiết.

6. Chỉ hỗ trợ một số mật độ cụ thể thay vì tất cả.

  • Android hỗ trợ rất nhiều loại thiết bị với rất nhiều mật độ màn hình khác nhau. Trong Android 4.4 (API cấp 19) trở lên, framework hỗ trợ các mật độ khác nhau như: ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi và xxxhdpi. Mặc dù Android hỗ trợ tất cả các loại mật độ này, bạn không cần phải cung cấp resource cho tất cả các mật độ màn hình. Nếu chỉ có một tỷ lệ nhỏ người dùng của bạn có các thiết bị có một mật độ cụ thể, hãy xem xét liệu bạn có cần đưa các mật độ đó vào ứng dụng của mình không hay là bỏ qua nó. Nếu bạn không cung cấp resource cho một mật độ màn hình cụ thể, Android sẽ tự động chia tỷ lệ 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 hình ảnh được thu nhỏ, bạn có giảm thiểu kích cỡ ứng dụng nhiều hơn bằng cách khai báo một biến thể duy nhất của hình ảnh trong drawable-nodpi/. Tuy vậy ứng dụng của bạn nên bao gồm ít nhất một biến thể hình ảnh xxhdpi.

7. Sử dụng đối tượng Drawable.

  • Một số hình ảnh không yêu cầu tài nguyên hình ảnh tĩnh. Thay vào đó, framework có thể tự động vẽ hình ảnh tại runtime. Các đối tượng Drawable (<shape> trong XML) chỉ chiếm một lượng không gian nhỏ trong APK của bạn và điều này sẽ giúp bạn giảm thiểu được kích cỡ ứng dụng. Ngoài ra, các đối tượng XML Drawable tạo ra các hình ảnh tuân thủ theo material design mà google đưa ra.

8. Sử dụng lại resources.

  • Bạn có thể tạo các resources riêng cho các biến thể của một hình ảnh, chẳng hạn như tô màu, tô bóng hoặc xoay của cùng một hình ảnh. Tuy nhiên, bạn nên sử dụng lại cùng một bộ tài nguyên, tùy chỉnh customize chúng khi cần trong thời gian chạy. Android cung cấp một số tiện ích để thay đổi màu sắc của một asset, bằng cách sử dụng các thuộc tính android: tint và tintMode 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 resources mà chỉ tương đương với một resources khác. Đoạn mã sau đây cung cấp một ví dụ về việc xoay ở giữa một hình ảnh và xoay 180 độ, việc này sẽ giúp bạn không phải tạo thêm một hình ảnh xoay ngược của một ảnh đã có:

9. Render hình ảnh từ code.

  • Bạn cũng có thể giảm kích thước APK của mình bằng cách hiển thị thủ công hình ảnh của mình. Việc render hình ảnh từ code giúp giải phóng không gian vì bạn không còn phải lưu trữ lưu trữ tệp hình ảnh trong APK của mình.
  • Ví dụ bạn có thể vẽ một đường thẳng bằng <shape> thay thế cho một hình ảnh.

10. Sử dụng aapt để tối ưu hóa file PNG.

  • Công cụ aapt có thể tối ưu hóa hình ảnh được đặt trong res/drawable/ với việc sử dụng nén không mất dữ liệu (lossless compression) trong quá trình xây dựng ứng dụng. Ví dụ: công cụ aapt có thể chuyển đổi một ảnh PNG không yêu cầu nhiều hơn 256 màu thành PNG với bảng màu 8 bit. Làm như vậy sẽ giữ cho hình ảnh có chất lượng tương đương nhưng dung lượng bộ nhớ nhỏ hơn rất nhiều.

Lưu ý khi sử dụng appt:

Công cụ aapt không thu nhỏ các tệp PNG trong thư mục asset/folder.

  • Công cụ appt có thể tối ưu các hình ảnh mà cần sử dụng ít hơn 256 màu.
  • Công cụ aapt có thể làm tăng các tệp PNG đã được nén. Để ngăn chặn điều này, bạn có thể sử dụng cruncherEnables trong Gradle ngăn chặn việc này.

11. Nén các file 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ác công cụ như pngcrushpngquant hoặc zopflipng. Tất cả các công cụ này có thể giảm kích thước tệp PNG trong khi vẫn giữ được chất lượng hình ảnh.
  • Công cụ pngcrush đặc biệt hiệu quả: Công cụ này lặp lại các bộ lọc PNG và các tham số zlib (Deflate), sử dụng từng tổ hợp bộ lọc và tham số để nén hình ảnh. Sau đó, nó chọn cấu hình mang lại đầu ra nén nhỏ nhất. Các công cụ trên bạn có thể dễ dàng add thêm vào project của mình.

12. Sử dụng định dạng WebP cho hình ảnh.

  • Thay vì sử dụng tệp PNG hoặc JPEG, bạn cũng có thể sử dụng định dạng tệp WebP cho hình ảnh của mình (API 13 trở lên). Định dạng WebP cung cấp khả năng nén mất dữ liệu (như JPEG) cũng như độ trong suốt (như PNG) nhưng có thể cung cấp khả năng nén tốt hơn so với định dạng JPEG hoặc PNG. Tuy nhiên đôi khi ảnh sau khi nén kích cỡ có thể tăng lên, bạn có thể theo dõi kích cỡ file trước và sau khi nén thông qua Android Studio một cách rất đơn giản.
  • Bạn có thể sử dụng Android studio để chuyển đổi nhanh các định dạng file khác qua WebP một cách nhanh chóng bằng cách click chuột phải vào file mà bạn muốn chuyển đổi và chọn Convert to WebP.

13. Sử dụng đồ họa định dạng vector.

  • Bạn có thể sử dụng đồ họa vector để tạo các biểu tượng độc lập với độ phân giải. Định dạng vector có thể kéo to nhỏ tùy ý mà không bị vỡ, các đường viền cũng không bị răng cưa. Dữ liệu có trong ảnh vector ít hơn ảnh bitmap, do đó ít tốn dung lượng lưu trữ hơn. Sử dụng những đồ họa này có thể làm giảm đáng kể kích cỡ file APK của bạn. Hình ảnh vector được thể hiện trong Android dưới dạng các đối tượng VectorDrawable. Với một đối tượng VectorDrawable, một tệp 100 byte có thể tạo ra một hình ảnh sắc nét theo kích thước của màn hình. Tuy nhiên, phải mất một khoảng thời gian đáng kể để hệ thống hiển thị từng đối tượng VectorDrawable và 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 sử dụng những đồ họa vector này chỉ khi hiển thị hình ảnh nhỏ.
  • Lưu ý: Để tối ưu hóa cho hiệu suất, một bộ đệm bitmap được tạo cho mỗi VectorDrawable. Do đó, tham chiếu đến cùng một VectorDrawable cũng có nghĩa là chia sẻ cùng một bộ đệm bitmap. Nếu các tham chiếu này không giống nhau về kích thước, bitmap sẽ được tạo lại và vẽ lại mỗi khi thay đổi kích thước. Vì vậy nếu VectorDrawable được sử dụng cho các kích thước khác nhau, sẽ hiệu quả hơn khi tạo nhiều VectorDrawables, một cho mỗi kích thước.

14. Giảm thiểu các mã code.

  • Có một số phương pháp bạn có thể sử dụng để giảm kích thước của mã Java và mã cơ sở trong ứng dụng của mình.
  • Xóa các mã được tự động sinh ra một cách không cần thiết . Ví dụ, nhiều công cụ bộ đệm giao thức tạo ra quá nhiều phương thức và lớp, con số này có thể tăng gấp đôi hoặc gấp ba kích thước ứng dụng của bạn.
  • Một enum có thể thêm kích thước khoảng 1,0 đến 1,4 KB vào tệp class.dex của ứng dụng của bạn điều này làm phình to ứng dụng của bạn. Nếu có thể, hãy cân nhắc sử dụng chú thích @IntDef và ProGuard để loại bỏ các enumerations và chuyển đổi chúng thành intergers. Chuyển đổi loại này giúp giữ lại tất cả các lợi ích an toàn loại của enums nhưng đồng thời giúp giảm kích thước của tệp.
  • Loại bỏ các debug. Bạn có thể sử dụng debug trong quá trình xây dựng ứng dụng nhưng nên loại bỏ chúng khi phát hành. Android có hỗ trợ việc này bằng công cụ arm-eabi-strip tool.
  • Không trích xuất các thư viện riêng. Khi xây dựng phiên bản phát hành của ứng dụng của bạn, hãy đóng gói các tệp không nén .so trong APK bằng cách đặt android: extractNativeLibs = "false" trong thẻ <application> trong file Manifest của bạn. Việc vô hiệu hóa này sẽ ngăn PackageManager sao chép các tệp .so từ APK sang hệ thống tệp trong khi cài đặt và có thêm lợi ích là làm cho các bản cập nhật của ứng dụng của bạn nhỏ hơn. Tuy nhiên bạn nên cân nhắc việc này vì việc này giúp cho bản cập nhật nhỏ hơn và kích cỡ ứng dụng khi cài đặt sẽ nhỏ hơn. Tuy nhiên bạn hoàn toàn có thể set giá trị true cho thuộc tính này và kích cỡ app tải xuống sẽ giảm đi vì các thư vien native sẽ được nén trong Apk tuy nhiên kích cỡ khi cài đặt thì không hề nhỏ và việc cài đặt app sẽ lâu hơn vì cần giải nén. Bạn có thể cân nhắc sử dụng tùy trường hợp.

15. Sử dụng Android App Bundles.

  • Android App Bundles (.aab) là gói ứng dụng mà bạn có thể xuất bản một ứng dụng mới một cachs thuận tiện hơn và quan trọng là nó giúp giảm thiểu kích cỡ ứng dụng của bạn.
  • Khi bạn đã tải lên tệp .aab của mình, Google Play sẽ sử dụng tệp đó để tạo ra các file như sau:
  • File APK cơ sở.File này chứa tất cả các mã và tài nguyên cần thiết để cung cấp chức năng cơ bản của ứng dụng của bạn. Bất cứ khi nào người dùng tải xuống ứng dụng của bạn, đây là file APK họ sẽ nhận được trước và mọi file APK tiếp theo sẽ phụ thuộc vào APK cơ sở này. Google Play tạo APK cơ sở từ ứng dụng, mô-đun hoặc mô-đun cơ sở dự án của bạn.
  • Cấu hình file APK: Mỗi khi ai đó tải xuống ứng dụng của bạn, Google Play sẽ sử dụng mô hình phân phối động mới, để phân phối APK cấu hình phù hợp với cấu hình thiết bị cụ thể đó. Google Play cũng có thể tạo một hoặc nhiều APK tính năng động.
  • Khi bạn xây dựng Gói ứng dụng, bạn có thể giảm kích thước APK của mình bằng cách tách các tính năng này thành các mô-đun tính năng động, sau đó người dùng có thể tải xuống theo yêu cầu. Nếu người dùng yêu cầu một mô-đun tính năng động, Phân phối động sẽ cung cấp cho họ APK tính năng động chỉ chứa mã và tài nguyên cần thiết để chạy tính năng cụ thể này, trên thiết bị cụ thể của người dùng.

Lưu ý:

  • Tính năng này chỉ hoạt động trên Android 5.0 trở lên (Api level 21)
  • Tải xuống các mô-đun theo yêu cầu hiện tại là bản beta nên có thể có lỗi xảy ra.
  • Theo như Goolge công bố thì sử dụng android app bundle có thể giúp giảm tới 35% kích cỡ của ứng dụng khi phát hành.

Người viết: Nguyễn Thanh Tùng

TopDev via Viblo