Vấn đề thường gặp và giải pháp

Tài liệu này là danh sách chưa đầy đủ về một số vấn đề không phải lỗi thường gặp nhất mà bạn có thể gặp phải khi sử dụng NDK, cũng như giải pháp khắc phục (nếu có).

Sử dụng _FILE_OFFSET_BITS=64 với các cấp độ API cũ

Trước khi có tiêu đề hợp nhất, NDK không hỗ trợ _FILE_OFFSET_BITS=64. Nếu bạn đã xác định mã này khi tạo ứng dụng, thì mã này sẽ bị ngầm bỏ qua. Tuỳ chọn _FILE_OFFSET_BITS=64 hiện được hỗ trợ khi có tiêu đề hợp nhất, nhưng trên các phiên bản Android cũ, rất ít API off_t có sẵn dưới dạng biến thể off64_t. Do đó, việc sử dụng tính năng này với cấp độ API cũ sẽ dẫn đến việc có ít chức năng hơn.

Vấn đề này được giải thích chi tiết trong bài đăng trên blog r16 và trong tài liệu bionic.

Vấn đề: Bản dựng của bạn yêu cầu các API không tồn tại trong minSdkVersion.

Giải pháp: Vô hiệu hoá _FILE_OFFSET_BITS=64 hoặc đưa ra (raise) minSdkVersion.

Định nghĩa không được khai báo hoặc ngầm ẩn của mmap

Bạn có thể thấy lỗi sau trong C++:

lỗi: sử dụng giá trị nhận dạng chưa được khai báo "mmap"

hoặc lỗi sau trong C:

cảnh báo: khai báo ngầm định hàm "mmap" không hợp lệ trong C99

Việc sử dụng _FILE_OFFSET_BITS=64 sẽ hướng dẫn thư viện C sử dụng mmap64 thay vì mmap. mmap64 không dùng được cho đến android-21. Nếu giá trị minSdkVersion thấp hơn 21 thì thư viện C không chứa mmap tương thích với _FILE_OFFSET_BITS=64, do đó, hàm này không dùng được.

minSdkVersion được đặt ở cấp cao hơn cấp độ API của thiết bị

Cấp độ API mà bạn xây dựng dựa trên NDK có ý nghĩa rất khác so với ý nghĩa của compileSdkVersion đối với Java. Cấp độ API NDK là cấp độ API được hỗ trợ tối thiểu của ứng dụng. Trong ndk-build, đây là chế độ cài đặt APP_PLATFORM của bạn. Với CMake, đây là -DANDROID_PLATFORM.

Vì các tệp tham chiếu đến các hàm thường được phân giải khi thư viện được tải thay vì khi được gọi lần đầu tiên, nên bạn không thể tham chiếu các API không phải lúc nào cũng hiện diện và đảm bảo việc sử dụng các API đó bằng các lần kiểm tra cấp độ API. Chỉ cần được đề cập đến, thì các API này phải có mặt.

Vấn đề: Cấp độ API NDK cao hơn API mà thiết bị của bạn hỗ trợ.

Giải pháp: Đặt cấp độ API NDK (APP_PLATFORM) thành phiên bản Android tối thiểu mà ứng dụng của bạn hỗ trợ.

Hệ thống xây dựng Cài đặt
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

Đối với các hệ thống xây dựng khác, hãy xem bài viết Sử dụng NDK với hệ thống xây dựng khác.

Không thể tìm các Ký hiệu __aeabi

Thông báo sau đây:

UnsatisfiedLinkError: dlopen không thành công: không tìm được ký hiệu "__aeabi_memcpy"

là một ví dụ về các lỗi thời gian chạy có thể xảy ra. Những lỗi này xuất hiện trong nhật ký khi bạn cố gắng tải thư viện gốc. Ký hiệu có thể là ký hiệu bất kỳ trong số __aeabi_*; __aeabi_memcpy__aeabi_memclr có vẻ là những ký hiệu phổ biến nhất.

Vấn đề này được nêu trong phần Vấn đề 126

Không tìm được ký hiệu rand

Đối với thông báo nhật ký lỗi sau đây:

UnsatisfiedLinkError: dlopen không thành công: không tìm được ký hiệu "rand"

Hãy xem câu trả lời chi tiết về Stack Overflow này.

Tham chiếu chưa xác định đến __atomic_*

Vấn đề: Một số ABI cần libatomic để cung cấp một số quy trình triển khai cho hoạt động nguyên tử.

Giải pháp: Thêm -latomic khi liên kết.

Đối với thông báo lỗi sau:

lỗi: tham chiếu chưa xác định đến "__atomic_exchange_4"

thì ký hiệu thực tế ở đây có thể là bất kỳ giá trị nào có tiền tố là __atomic_.

RTTI/ngoại lệ không hoạt động trên các ranh giới của thư viện

Vấn đề: Các ngoại lệ không bị phát hiện khi được đưa ra (throw) trên các ranh giới của thư viện dùng chung hoặc dynamic_cast không thành công.

Giải pháp: Thêm hàm chính vào các kiểu. Hàm chính là hàm ảo không thuần tuý, ngoài giới hạn đầu tiên cho một kiểu. Để biết ví dụ, hãy xem phần thảo luận về Vấn đề 533.

ABI C++ cho biết rằng hai đối tượng có cùng một kiểu khi và chỉ khi các con trỏ type_info của hai đối tượng này giống nhau. Các ngoại lệ chỉ có thể được phát hiện nếu type_info dùng để phát hiện khớp với ngoại lệ được đưa ra. Quy tắc tương tự cũng áp dụng cho dynamic_cast.

Khi một kiểu không có hàm chính, typeinfo của kiểu đó sẽ được phát hành dưới dạng ký hiệu yếu và thông tin kiểu khớp được hợp nhất khi thư viện được tải. Nếu tải thư viện theo phương thức động sau khi đã tải tệp thực thi (nói cách khác là thông qua dlopen hoặc System.loadLibrary), trình tải có thể không hợp nhất được thông tin kiểu đối với thư viện đã tải. Khi điều này xảy ra, hai kiểu đó không được coi là ngang bằng.

Sử dụng thư viện tạo sẵn không khớp

Khi sử dụng thư viện tạo sẵn – thường là thư viện của bên thứ ba – trong ứng dụng, bạn cần cẩn trọng hơn. Nói chung, hãy lưu ý các quy tắc sau:

  • Cấp độ API tối thiểu của ứng dụng thu được là cấp tối đa của minSdkVersion trong số tất cả thư viện của ứng dụng.

    Nếu minSdkVersion của bạn là 16 nhưng bạn đang sử dụng một thư viện tạo sẵn được tạo dựa trên cấp 21, thì cấp độ API tối thiểu của ứng dụng thu được sẽ là 21. Việc không tuân thủ quy tắc này sẽ hiển thị tại thời điểm xây dựng nếu thư viện tạo sẵn là thư viện tĩnh, nhưng có thể không xuất hiện cho đến thời gian chạy của các thư viện dùng chung tạo sẵn.

  • Tất cả các thư viện phải được tạo bằng cùng một phiên bản NDK.

    Quy tắc này linh hoạt hơn một chút so với hầu hết các quy tắc khác vì rất hiếm khi xảy ra sự cố, nhưng khả năng tương thích giữa các thư viện được tạo bằng các phiên bản lớn khác nhau của NDK không được đảm bảo. ABI C++ không ổn định và từng thay đổi trước đây.

  • Các ứng dụng có nhiều thư viện dùng chung phải sử dụng một STL dùng chung.

    Tương tự như với STL không khớp, nếu cẩn thận, bạn có thể tránh các vấn đề do lỗi này gây ra. Nhưng tốt hơn hết là tránh sự cố này. Cách tốt nhất để tránh vấn đề này là tránh có nhiều thư viện dùng chung trong ứng dụng.