Questions? (+84) 247 300 6665 or hr@hri.com.vn

10 lỗi lập trình viên Android thường mắc phải

22/11/2019

Android là một nền tảng được nhiều người ưa thích, miễn phí và dễ tùy biến nên đã nhanh chóng phát triển và có mặt trên nhiều loại thiết bị di động như: điện thoại, máy tính bảng, đồng hồ thông minh, tivi, ô tô…
Sau sự ra mắt của AOSP và bản update Lollipop, Android đã mang lại cho người dùng khá nhiều kì vọng. Phong cách thiết kế Material hứa hẹn đem lại trải nghiệm tốt hơn.
Có cả ngàn thiết bị Android khác nhau kích thước màn hình, chip xử lý, cấu hình phần cứng và phiên bản phần mềm. Sự đa dạng ấy dẫn tới việc ứng dụng Android mà bạn viết có thể không hoạt động được trên các thiết bị khác nhau cho dù bạn có là một lập trình viên Android nhiều kinh nghiệm.
Bỏ qua sự khác biệt kia, phần lớn lỗi mọi người biết đến lại là lỗi logic. Những lỗi này có thể dễ dàng xử lý nếu chúng ta làm đúng từ những điều cơ bản.
Dưới đây là danh sách 10 lỗi thường thấy ở các lập trình viên Android.

1. Cố gắng bắt chước giao diện iOS

Ngày nay lỗi này đã trở nên ít phổ biến hơn (một phần vì khách hàng đã nhận ra rằng Apple đã thiết lập những tiêu chuẩn thiết kế từ lâu rồi). Nhưng dù có thế thì chúng ta vẫn đang và sẽ tiếp tục thấy những ứng dụng sao chép lại từ iOS.
Người dùng Android đã quen thuộc với nền tảng Android, việc cố gắng nhồi nhét các chuẩn thiết kế của iOS vào Android sẽ rất tồi tệ. Nếu bạn không có lí do cực kì đáng giá nào để phá vỡ các nguyên tắc thì đừng làm thế.
Đây là một số ví dụ phổ biến cho lỗi này:

  1. Bạn không nên tạo các tab cố định mà chúng không ở phía dưới (ví dụ: Instagram).
  2. Các Icon thông báo hệ thống không nên màu mè
  3. Icon của ứng dụng không nên có khung viền chữ nhật bo góc (trừ khi logo của bạn vốn đã có nó, ví dụ như Facebook)
  4. Màn hình load ứng dụng (splash screen) chỉ nên dùng khi thiết lập lần đầu hoặc giới thiệu, không nền dùng trong các trường hợp khác.
  5. Danh sách không nên có đánh dấu

Trên đây chỉ là một số ít trong số rất nhiều vấn đề lặt vặt có thể phá hỏng trải nghiệm người dùng.

2. Lập trình viên chỉ thử ứng dụng trên đúng thiết bị Android mình có

Trừ khi bạn muốn tạo ra một ứng dụng phổ biến cho riêng loại thiết bị nào đó, nếu không thì ứng dụng của bạn có thể sẽ trông khá tệ trên các thiết bị khác. Bạn nên lưu ý các vấn đề sau:

  • Density-independent pixels (dp) khác với pixel (px).
  • Resources được thêm nhiều lần vào tài khoản đối với mật độ và định hướng khác nhau.
  • 9-patch drawables sẽ được kéo dãn cho khít với màn hình.

Chắc chắn bạn không có cả ngàn thiết bị để thử, Android Emulator sẽ giúp giả lập gần đúng cấu hình thiết bị bạn muốn thử. Android Emulator có thể chọn được kích thước màn hình, bộ nhớ, chức năng cảm biến…. Còn nếu bạn muốn một trình giả lập tốt hơn nữa, hãy thử Genymotion, emulator chạy nhanh và có nhiều cài đặt giả lập phổ biến cho các thiết bị.
Còn một vấn đề nữa, bạn có thử xoay ngang dọc điện thoại hoặc emulator khi chạy thử ứng dụng chưa? Bạn sẽ thấy ngay rất nhiều lỗi giao diện phải sửa.

3. Không dùng Intent

Intent là một trong những thành phần chủ chốt của Android. Nó là một cách để truyền dữ liệu giữa các phần của một ứng dụng hoặc hơn thế nữa, giữa các ứng dụng khác nhau trong một hệ thống.
Giả sử bạn có một ứng dụng gallery ảnh cho phép chia sẻ link download ảnh qua SMS. Lựa chọn nào sau đây sẽ logic hơn?
Lựa chọn 1:

  • Yêu cầu cấp quyền SEND_SMS?
  • Viết code gửi SMS theo cách của bạn, sử dụng SmsManager
  • Giải thích cho người dùng vì sao ứng dụng của bạn cần truy cập các dịch phụ có thể tính phí và vì sao người dùng phải cấp quyền truy cập đó cho ứng dụng của bạn.

Lựa chọn 2:

  • Bắt đầu một SMS Intent và để việc gửi Sms cho một ứng dụng chuyên gửi Sms

Nếu bạn còn lưỡng lự thì câu trả lời là: cách thứ 2 là cách tốt nhất.
Phương pháp này có thể áp dụng với hầu hết mọi thứ, ví như: chia sẻ nội dung, chụp ảnh, quay video, chọn các liên lạc, thêm sự kiện, …
Trừ trường hợp có một lý do hợp lý để tự làm phần chức năng bổ sung (ví dụ: camera có áp dụng các bộ lọc chỉnh ảnh), nếu không thì cứ dùng Intent. Nó sẽ tiết kiệm cực kì nhiều thời gian lập tình và giảm bớt các yêu cầu cấp quyền không cần thiết trong file AndroidManifest.xml.

4. Không dùng Fragments

Một thời gian trước trên Honeycomb, Android giới thiệu khái niệm Fragments. Bạn có thể xem nó như là các khối được xây dựng riêng biệt với vòng đời riêng trong một Activity. Nó hỗ trợ rất nhiều trong việc tối ưu cho các loại màn hình, đồng thời dễ dàng được quản lý bởi activity cha, có thể sử dụng lại, kết hợp và bố trí theo ý muốn.
Chạy từng activity riêng cho mỗi màn hình ứng dụng sẽ có hiệu quả rất tệ khi hệ thống phải cố lưu trữ chúng trong bộ nhớ lâu hết mức có thể. Tắt một cái trong số đó cũng không giải phóng các tài nguyên được sử dụng bởi những cái còn lại.

Nếu bạn không muốn đào sâu nghiên cứu về core Android hay phản đối việc sử dụng fragment thì bạn nên dùng fragments bất cứ khi nào có thể. Cơ bản thì fragments và cursor loaders có mục đích tốt nhưng cách thực hiện thì khá thô.

5. Blocking Main Thread (tác vụ chạy lâu cản trở luồng thực thi chính)

Main thread (luống chính) có một mục đích duy nhất: phản hồi tương tác của người dùng và cập nhật giao diện.
Mặc dù theo các phương pháp đo đếm một cách khoa học về tốc độ khung hình (frame rate) thì mắt/não của chúng ta có khả năng cảm nhận rất phức tạp và chịu ảnh hưởng của nhiều yếu tố nhưng có một nguyên tắc chung là cứ cái gì dưới 24 fps (khung hình/giây) với độ trễ trên 100ms thì được coi là không mượt mà.
Có nghĩa là hành vi của người dùng sẽ có một khoảng trễ và ứng dụng Android mà bạn lập trình sẽ dừng việc phản hồi lại. Bỏ qua yếu tố cách sử dụng của người dùng dẫn tới ứng dụng chạy không như ý, người dùng của bạn chỉ cần thấy ứng dụng không chạy như họ muốn là sẽ có các nhận xét tiêu cực.
Tệ hơn nữa, nếu main thread bị block một lúc (5 giây đối với các Activity, 10 giây đối với Broadcast Receivers),ANR sẽ xảy ra.

Vấn đề này từng rất phổ biến trên Android 2.x trong khi đó các phiên bản mới hơn của hệ thống không cho phép bạn tạo các lời gọi network trong main thread.
Để tránh bị lỗi này, tốt nhất là bạn luôn sử dụng worker/background thread cho các trường hợp sau: gọi network (network call), nạp hình ảnh (bitmap loading), xử lý ảnh (image processing), truy vấn dữ liệu (database querying), đọc/ghi vào thẻ nhớ (SD reading/writing).

6. Tự code lại những thứ người khác đã làm

Một số bạn muốn tự code phần giao tiếp với server trong một background thread, đó chưa chắc là một lựa chọn tốt. Gọi network, nạp ảnh, truy cập cơ sở dữ liệu, parse JSON hay đăng nhập mạng xã hội, đó là những thứ ứng dụng của bạn thường xuyên làm nhất. Không chỉ ứng dụng của bạn mà tất cả các ứng dụng khác cũng thế. Vậy viết lại làm gì? Hãy nhớ là Android đã phát triển thành một nền tảng ổn định và có nhiều thư viện có sẵn hỗ trợ cho bạn. Ví dụ:

  1. Dùng gradle để làm hệ thống build.
  2. Dùng Retrofit / Volley cho network calls.
  3. Dùng Picasso để nạp ảnh.
  4. Dùng Gson / Jackson để parse JSON.
  5. Dùng common implementations để xử lý đăng nhập mạng xã hội.

Nếu bạn nghĩ cần phải làm điều gì đó, rất có thể thứ đó đã được viết, được kiểm thử rồi và đang được dùng rộng rãi. Hãy cứ thử tìm kiếm và đọc các bài hướng dẫn lập trình Android trước khi tự làm cái gì đó.

7. Không giả sử tác vụ thành công để xử lý sớm

Qua lỗi thứ sáu, chúng ta có thể sử dụng các thư viện có sẵn để xử lý các tác vụ phức tạp. Nhưng người dùng vẫn sẽ phải chờ đợi, đó là điều không thể tránh khỏi. Có nhiều vấn đề có thể xảy ra: các gói không được gửi đi, không được xử lý hay nhận ngay. Cũng có thể là phải chờ các vòng lặp, lỗi network, mất gói dữ liệu, hàng loạt thứ khiến cho người dùng phải chờ đợi rất lâu.
Tuy nhiên, trong hầu hết trường hợp, các lỗi trên ít xảy ra và quá trình thực thi code thành công nhiều hơn thất bại. Vậy sao lại phải chờ server phản hồi trước khi request thành công? Sẽ tốt hơn nhiều nếu bạn cứ giả sử như lời gọi đó đã thành công và xử lý trường hợp thất bại sau đó. Ví dụ khi người dùng like một bài viết, ta cho số like tăng luôn, nếu chẳng may sự kiện đó bị lỗi thì ta sẽ thông báo cho người dùng.
Trong thế giới hiện đại hối hả, con người luôn muốn được phản hồi ngay lập tức. Mọi người thường không thích phải chờ đợi, cũng giống như lũ trẻ không thích ngồi trong lớp học những thứ chưa chắc đã giúp ích gì cho chúng trong tương lai. Ứng dụng của bạn của bắt kịp được tâm lý người dùng.

8. Không hiểu về Bitmap

Người dùng yêu thích nội dung được hiển thị, đặc biệt là khi nó được trình bày đẹp và gọn gàng. Ví dụ, hình ảnh là một dạng nội dung thu hút, mỗi bức ảnh có thể truyền tải thông tin thay cho hàng ngàn câu chữ. Nó cũng là nội dung gây tốn bộ nhớ, tốn rất nhiều.
Trước khi một hình ảnh được hiển thị ra màn hình, nó phải được nạp vào bộ nhớ.Từ khi bitmap được sử dụng một cách phổ biến để nạp ảnh, chúng ta đã có nhiều hướng dẫn lập trình để thực hiện toàn bộ quá trình đó:
Giả sử bạn muốn hiển thị một hình ảnh vừa chụp từ camera lên màn hình. Tổng dung lượng bộ nhớ cần thiết cho công việc này được tính toán theo công thức:

Trong công thức trên, vì sao lại là nhân 4? Thiết lập bitmap phổ biến nhất và được khuyến khích sử dụng làARGB_8888. Như vậy có nghĩa là với mỗi điểm ảnh mà ta vẽ ra, ta cần 8 bit (1 byte) cho 4 kênh màu alpha, red, green, blue trong bộ nhớ để có thể hiển thị đúng hình ảnh. Có một số thiết lập khác như RGB_565 yêu cầu ít bộ nhớ hơn ARGB_8888 một nửa, nhưng mất phần hiển thị trong suốt (transparent) và giảm độ chính xác về màu (có thể dẫn tới có thêm các đường kẻ màu xanh trên màn hình).
Giả sử như bạn có một thiết bị đời mới màn hình full HD với camera 12MP. Mỗi tấm ảnh bạn chụp có kích thước 4000×3000 pixel, dung lượng bộ nhớ hao tốn cho việc hiển thị nó ra sẽ là: 4 bytes * 4000 * 3000 = 48 MB.
48MB để hiện một tấm ảnh, con số này có vẻ khá khủng khiếp.
Giờ ta thử cân nhắc tới chênh lệch về độ phân giải màn hình. Nếu muốn hiện một ảnh 4000×3000 như trên lên màn hình 1920×1080 pixel, trong trường hợp tệ nhất (hiện ảnh toàn màn hình) bạn không nên cấp nhiều RAM hơn con số sau: 4 * 1920 * 1080 = 8.3 MB.
Nói chung, bạn nên làm theo các hướng dẫn lập trình Android về hiệu suất hiển thị hình ảnh:

  1. Tính toán khung màn hình bạn muốn hiện ảnh.
  2. Co dãn ảnh hay crop ảnh cho phù hợp.
  3. Chỉ hiển thị cái gì có thể hiển thị.

9. Dùng hệ thống view quá nhiều cấp

Đối với Android, layout có thể được thể hiện qua XML. Để vẽ ra các nội dung cần thiết, XML cần được phân tích, màn hình cần tính toán kích cỡ và mọi thành phần đều phải được đặt ở vị trí phù hợp. Đó là một quá trình hao tốn thời gian và tài nguyên hệ thống để tối ưu mọi thứ.
Dưới đây là cách ListView (hay mới hơn là RecyclerView) hoạt động.
Nếu một layout đã được tăng kích thước một lần, hệ thống sẽ dùng lại nó. Tuy vậy, việc tăng kích thước layout vẫn có thể xảy ra ở một vài thời điểm.
Ví dụ bạn muốn tạo một lưới 3×3 với nội dung ảnh. Bạn có thể dùng một LinearLayout dọc có chứa 3 LinearLayout có cùng giá trị weight, mỗi cái lại có 3 ImageViews có cùng giá trị weight.

Với cách này kết quả sẽ là một cảnh báo “nested weights are bad for performance”.
Có một cách nói trong giới lập trình Android rằng:

“Nếu không quan tâm tới hiệu suất thì cấu trúc nào cũng như nhau”.

Trong trường hợp này RelativeLayout hoặc GridLayout sẽ thay thế được kiểu LinearLayouts lồng nhau một cách hiệu quả.

10. Không thiết lập giá trị minSdkVersion bằng 14

Đây thực ra cũng không phải là lỗi nhưng lại là một thói quen xấu.
Android 2.x là một cột mốc quan trọng trong sự phát triển của nền tảng Android nhưng có một vài thứ nên bỏ đi. Việc hỗ trợ các thiết bị cũ gây phức tạp cho bảo trì code và hạn chế quá trình phát triển.
Những con số thống kê rất rõ ràng đã cho thấy người dùng đang mong chờ nhiều điều ở tương lai và các lập trình viên nên bắt kịp xử phát triển của thời đại.
Vấn đề này không áp dụng đối với một số thị trường lớn với các thiết bị cũ (ví như Ấn Độ), và nếu không đặt minSdkVersion bằng 14 đối với ứng dụng Facebook thì vài triệu người dùng yêu thích mạng xã hội đã bị bỏ qua. Nhưng nếu bạn muốn một khởi đầu mới và tạo ra trải nghiệm người dùng tốt nhất thì nên xem xét loại bỏ quá khứ. Khiến người dùng cảm thấy cần phải nâng cấp thiết bị, nâng cấp hệ điều hành để có trải nghiệm tốt hơn, như vậy mới thúc đẩy họ đến với các phiên bản cao cấp của Android và rồi tiêu tiền cho mục đích đó.

Kết luận

Android là một nền nảng mạnh mẽ và phát triển rất nhanh chóng. Các lập trình viên Android nên thường xuyên tìm cách nâng cao kỹ năng của mình và giảm thiểu sai sót.

Share this job
BACK TO BLOG