Android Runtime (ART) là môi trường thời gian chạy mặc định cho các thiết bị chạy Android 5.0 (API cấp 21) trở lên. Môi trường thời gian chạy này cung cấp nhiều tính năng giúp cải thiện hiệu suất và độ mượt của nền tảng Android cũng như các ứng dụng. Bạn có thể tìm thêm thông tin về các tính năng mới của ART trong phần Giới thiệu ART.
Tuy nhiên, một số kỹ thuật hoạt động trên Dalvik không hoạt động trên ART. Chiến dịch này cho bạn biết về những điều cần lưu ý khi di chuyển tương thích với ART. Hầu hết các ứng dụng sẽ chỉ hoạt động khi chạy cùng (theo giờ ART)
Giải quyết các vấn đề về thu gom rác (GC)
Trong Dalvik, các ứng dụng thường thấy hữu ích khi gọi một cách rõ ràng
System.gc()
để nhắc thu thập rác (GC). Thông tin này phải là
ít cần thiết hơn nhiều với ART, đặc biệt là khi bạn đang gọi hoạt động thu gom rác
để ngăn loại GC_FOR_ALLOC
hoặc giảm sự phân mảnh. Bạn có thể xác minh thời gian chạy nào đang được sử dụng
bằng cách gọi System.getProperty("java.vm.version")
. Nếu đang sử dụng ART, giá trị của thuộc tính này
từ "2.0.0"
trở lên.
ART sử dụng trình thu thập đồng thời sao chép (CC) để đồng thời nén vùng nhớ khối xếp Java. Do đó, bạn nên tránh sử dụng các kỹ thuật không tương thích với tính năng nén GC (chẳng hạn như lưu con trỏ đến đối tượng dữ liệu thực thể). Điều này đặc biệt quan trọng đối với những ứng dụng tận dụng Giao diện gốc Java (JNI). Để biết thêm thông tin, hãy xem bài viết Ngăn ngừa các vấn đề về JNI.
Ngăn chặn các vấn đề về JNI
JNI của ART có phần nghiêm ngặt hơn so với JNI của Dalvik. Đó là một ý tưởng đặc biệt hay để sử dụng chế độ CheckJNI nhằm phát hiện các vấn đề thường gặp. Nếu ứng dụng của bạn sử dụng C/C++ , bạn nên tham khảo bài viết sau:
Gỡ lỗi JNI Android với CheckJNI
Kiểm tra mã JNI để tìm các sự cố thu thập rác
Trình thu thập Sao chép đồng thời (CC) có thể di chuyển các đối tượng trong bộ nhớ để nén. Nếu bạn sử dụng mã C/C++, đừng thực hiện các thao tác không tương thích với việc nén GC. Chúng tôi đã nâng cao CheckJNI để xác định một số vấn đề tiềm ẩn (như mô tả trong JNI Các thay đổi về tệp đối chiếu cục bộ trong ICS).
Một khía cạnh cụ thể cần lưu ý là việc sử dụng
Get...ArrayElements()
và Release...ArrayElements()
. Trong thời gian chạy với GC không nén,
Hàm Get...ArrayElements()
thường trả về một tham chiếu đến
bộ nhớ thực sao lưu đối tượng mảng. Nếu bạn thực hiện thay đổi đối với một trong
trả về các phần tử mảng, đối tượng mảng đó tự thay đổi (và các đối số
đến Release...ArrayElements()
thường bị bỏ qua). Tuy nhiên, nếu
thu gọn GC đang được sử dụng, các hàm Get...ArrayElements()
có thể
trả về một bản sao của bộ nhớ. Nếu bạn lạm dụng tệp tham chiếu khi nén GC là
trong quá trình sử dụng, điều này có thể dẫn đến hỏng bộ nhớ hoặc các vấn đề khác. Ví dụ:
- Nếu bạn thực hiện bất kỳ thay đổi nào đối với các phần tử mảng được trả về, bạn phải gọi hàm
hàm
Release...ArrayElements()
thích hợp khi bạn hoàn tất, để đảm bảo những thay đổi bạn đã thực hiện được sao chép chính xác trở lại nền tảng mảng. - Khi bạn huỷ bỏ các phần tử mảng bộ nhớ, bạn phải sử dụng
, tuỳ thuộc vào những thay đổi bạn đã thực hiện:
- Nếu bạn không thực hiện bất kỳ thay đổi nào đối với các phần tử mảng, hãy sử dụng
Chế độ
JNI_ABORT
giải phóng bộ nhớ mà không cần sao chép thay đổi trở lại đối tượng mảng cơ bản. - Nếu bạn đã thay đổi mảng và không cần tham chiếu bất kỳ
khác, hãy sử dụng mã
0
(cập nhật đối tượng mảng và giải phóng bản sao của kỷ niệm đó). - Nếu bạn thực hiện thay đổi đối với mảng mà bạn muốn cam kết và muốn
để giữ bản sao của mảng, hãy sử dụng
JNI_COMMIT
(nội dung cập nhật đối tượng mảng cơ bản và giữ lại bản sao).
- Nếu bạn không thực hiện bất kỳ thay đổi nào đối với các phần tử mảng, hãy sử dụng
Chế độ
- Khi bạn gọi
Release...ArrayElements()
, hãy trả về cùng một kết quả con trỏ mà ban đầuGet...ArrayElements()
trả về. Cho ví dụ: việc tăng con trỏ ban đầu (để quét qua phần tử mảng trả về), sau đó chuyển con trỏ tăng dần đếnRelease...ArrayElements()
. Việc truyền con trỏ đã sửa đổi này có thể khiến bộ nhớ được giải phóng không chính xác, dẫn đến hỏng bộ nhớ.
Xử lý lỗi
JNI của ART sẽ gửi lỗi trong một số trường hợp mà Dalvik thì không. (Một lần xin nhắc lại, bạn có thể phát hiện nhiều trường hợp như vậy bằng cách kiểm thử bằng CheckJNI.)
Ví dụ: nếu RegisterNatives
được gọi bằng một phương thức
không tồn tại (có thể vì phương thức này đã bị một công cụ như xoá bỏ
ProGuard), ART hiện đang gửi NoSuchMethodError
đúng cách:
08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main 08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError: no static or non-static method "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I" 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.nativeLoad(Native Method) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.doLoad(Runtime.java:421) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.loadLibrary(Runtime.java:362) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.System.loadLibrary(System.java:526)
ART cũng ghi lại lỗi (hiển thị trong logcat) nếu RegisterNatives
là
được gọi mà không có phương thức nào:
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
Ngoài ra, JNI hàm GetFieldID()
và
GetStaticFieldID()
hiện đang gửi NoSuchFieldError
đúng cách
thay vì chỉ trả về giá trị rỗng. Tương tự, GetMethodID()
và
GetStaticMethodID()
hiện đang gửi NoSuchMethodError
đúng cách.
Điều này có thể dẫn đến lỗi CheckJNI do các ngoại lệ chưa được xử lý hoặc
ngoại lệ được gửi cho phương thức gọi Java của mã gốc. Điều này giúp
đặc biệt quan trọng khi kiểm thử các ứng dụng tương thích với ART bằng chế độ CheckJNI.
ART dự kiến người dùng của các phương thức CallNonvirtual...Method()
JNI
(chẳng hạn như CallNonvirtualVoidMethod()
) để sử dụng phần khai báo của phương thức
lớp, không phải lớp con, theo yêu cầu của quy cách JNI.
Tránh các vấn đề về kích thước ngăn xếp
Dalvik có các ngăn xếp riêng cho mã gốc và mã Java, với Java mặc định
kích thước ngăn xếp là 32KB và kích thước ngăn xếp gốc mặc định là 1MB. ART có một giao diện người dùng hợp nhất
ngăn xếp để có vị trí địa phương tốt hơn. Thông thường, ngăn xếp ART Thread
kích thước phải gần bằng với Dalvik. Tuy nhiên, nếu bạn rõ ràng
đặt kích thước ngăn xếp, bạn có thể cần phải xem lại các giá trị đó cho các ứng dụng đang chạy trong
(theo giờ ART)
- Trong Java, hãy xem lại các lệnh gọi đến hàm khởi tạo
Thread
chỉ định một ngăn xếp rõ ràng kích thước. Ví dụ: bạn sẽ cần tăng kích thước nếuStackOverflowError
xảy ra. - Trong C/C++, hãy xem lại cách sử dụng
pthread_attr_setstack()
vàpthread_attr_setstacksize()
cho các luồng cũng chạy mã Java qua JNI. Dưới đây là ví dụ về lỗi được ghi lại khi một ứng dụng cố gắng gọi JNIAttachCurrentThread()
khi kích thước của pthread quá nhỏ:F/art: art/runtime/thread.cc:435] Attempt to attach a thread with a too-small stack (16384 bytes)
Thay đổi về mô hình đối tượng
Các lớp con Dalvik được phép ghi đè các phương thức riêng tư của gói. ART sẽ đưa ra cảnh báo trong những trường hợp sau:
Before Android 4.1, method void com.foo.Bar.quux() would have incorrectly overridden the package-private method in com.quux.Quux
Nếu bạn có ý định ghi đè phương thức của một lớp trong một gói khác, hãy khai báo
dưới dạng public
hoặc protected
.
Object
hiện có các trường riêng tư. Ứng dụng suy ngẫm về các trường
trong hệ phân cấp lớp của mình nên cẩn thận để không cố gắng xem xét
trường Object
. Ví dụ: nếu bạn đang lặp lại một lớp
như một phần của khung chuyển đổi tuần tự, hãy dừng khi
Class.getSuperclass() == java.lang.Object.class
thay vì tiếp tục cho đến khi phương thức này trả về null
.
Proxy InvocationHandler.invoke()
hiện nhận được null
nếu không có
đối số thay vì một mảng trống. Hành vi này đã được ghi nhận trước đây nhưng
không được xử lý chính xác trong Dalvik. Các phiên bản trước của Mockito gặp khó khăn
điều này, vì vậy, hãy sử dụng phiên bản Mockito đã cập nhật khi thử nghiệm bằng ART.
Khắc phục sự cố biên dịch AOT
Hoạt động biên dịch Java trước khi sử dụng (AOT) của ART phải hoạt động được với mọi Java tiêu chuẩn
. Quá trình biên dịch do ART thực hiện
Công cụ dex2oat
; nếu bạn gặp phải bất kỳ vấn đề nào liên quan đến
dex2oat
khi cài đặt, hãy cho chúng tôi biết (xem Báo cáo sự cố) để chúng tôi có thể khắc phục chúng nhanh nhất
nhất có thể. Một số vấn đề cần lưu ý:
- ART thực hiện quy trình xác minh mã byte chặt chẽ hơn tại thời điểm cài đặt so với Dalvik. Mã do các công cụ xây dựng của Android tạo ra sẽ không có vấn đề gì. Tuy nhiên, một số các công cụ hậu xử lý (đặc biệt là các công cụ thực hiện làm rối mã nguồn) có thể các tệp không hợp lệ được Dalvik dung lượng nhưng bị ART từ chối. Chúng tôi đã làm việc với các nhà cung cấp công cụ để tìm và khắc phục các vấn đề như vậy. Trong nhiều trường hợp, việc các phiên bản công cụ mới nhất cũng như việc tạo lại tệp DEX vấn đề.
- Một số vấn đề điển hình được trình xác minh ART gắn cờ bao gồm:
- luồng kiểm soát không hợp lệ
- không cân bằng
monitorenter
/monitorexit
- Kích thước danh sách loại thông số có độ dài 0
- Một số ứng dụng có phần phụ thuộc trên tệp
.odex
đã cài đặt ở định dạng/system/framework
,/data/dalvik-cache
hoặc trong thư mục đầu ra được tối ưu hoá củaDexClassLoader
. Các các tệp này hiện là tệp ELF chứ không phải là dạng tệp DEX mở rộng. Trong khi ART cố gắng để tuân thủ cùng các quy tắc đặt tên và khoá như Dalvik, các ứng dụng không được phụ thuộc về định dạng tệp; định dạng có thể thay đổi mà không cần thông báo.Lưu ý: Trong Android 8.0 (API cấp 26) và cao hơn, thư mục đầu ra được tối ưu hoá
DexClassLoader
không được dùng nữa. Để biết thêm thông tin, hãy xem tài liệu dành choDexClassLoader()
hàm khởi tạo.
Sự cố về báo cáo
Nếu bạn gặp những vấn đề không liên quan đến vấn đề về JNI của ứng dụng, hãy báo cáo
chúng thông qua Công cụ theo dõi lỗi của dự án nguồn mở Android tại https://code.google.com/p/android/issues/list.
Thêm "adb bugreport"
và một đường liên kết đến ứng dụng trong Google
Cửa hàng Play nếu có. Hoặc nếu có thể, hãy đính kèm một tệp APK có khả năng tái tạo
vấn đề. Xin lưu ý rằng các vấn đề (bao gồm cả tệp đính kèm) sẽ được trình bày công khai
hiển thị.