Trước khi bạn bắt đầu, hãy xem thông tin tổng quan về Dịch vụ ART.
Kể từ Android 14, tính năng biên dịch AOT trên thiết bị cho các ứng dụng (còn gọi là dexopt) do Dịch vụ ART xử lý. Dịch vụ ART là một phần của ART mô-đun và bạn có thể tuỳ chỉnh nó thông qua các thuộc tính và API của hệ thống.
Thuộc tính hệ thống
ART Service hỗ trợ tất cả các dịch vụ tuỳ chọn dex2oat.
Ngoài ra, ART Service hỗ trợ các thuộc tính hệ thống sau:
pm.dexopt.<lý>
Đây là một tập hợp các thuộc tính hệ thống giúp xác định bộ lọc mặc định của trình biên dịch cho mọi lý do biên dịch định sẵn như mô tả trong các trường hợp sử dụng ngoại lệ.
Để biết thêm thông tin, hãy xem Bộ lọc trình biên dịch.
Các giá trị mặc định chuẩn là:
pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify
pm.dexopt.shared (mặc định: tốc độ)
Đây là bộ lọc trình biên dịch dự phòng cho những ứng dụng được các ứng dụng khác dùng.
Theo nguyên tắc, Dịch vụ ART thực hiện việc biên dịch theo hồ sơ (speed-profile
) cho
tất cả ứng dụng khi có thể, thường là trong quá trình dexopt ở chế độ nền. Tuy nhiên, có một số
một số ứng dụng mà các ứng dụng khác dùng (thông qua <uses-library>
hoặc đã tải
linh động bằng cách sử dụng Context#createPackageContext
với
CONTEXT_INCLUDE_CODE
). Những ứng dụng như vậy không thể sử dụng
vì lý do bảo mật.
Đối với một ứng dụng như vậy, nếu yêu cầu biên dịch theo hồ sơ, trước tiên, hãy sử dụng Dịch vụ ART
cố gắng sử dụng hồ sơ trên đám mây. Nếu hồ sơ trên đám mây không tồn tại, Dịch vụ ART
quay lại để sử dụng bộ lọc trình biên dịch do pm.dexopt.shared
chỉ định.
Nếu quá trình biên dịch yêu cầu không theo hướng dẫn của hồ sơ, thuộc tính này sẽ không có hiệu lực.
pm.dexopt.<reason>.concurrency (mặc định: 1)
Đây là số lệnh gọi dex2oat cho quá trình biên dịch xác định trước nhất định
lý do (first-boot
, boot-after-ota
, boot-after-mainline-update
và
bg-dexopt
).
Lưu ý rằng hiệu quả của tuỳ chọn này được kết hợp với
tuỳ chọn sử dụng tài nguyên dex2oat (dalvik.vm.*dex2oat-threads
,
dalvik.vm.*dex2oat-cpu-set
và hồ sơ công việc):
dalvik.vm.*dex2oat-threads
kiểm soát số lượng luồng cho mỗi dex2oat lệnh gọi, trong khipm.dexopt.<reason>.concurrency
kiểm soát số lượng lệnh gọi dex2oat. Tức là số lượng luồng đồng thời tối đa là tích của hai tính chất hệ thống.dalvik.vm.*dex2oat-cpu-set
và các hồ sơ tác vụ luôn liên kết với lõi CPU mức sử dụng, bất kể số lượng tối đa các luồng đồng thời (được thảo luận) ở trên).
Một lệnh gọi dex2oat duy nhất có thể không sử dụng hết tất cả các lõi CPU, bất kể
trong tổng số dalvik.vm.*dex2oat-threads
. Do đó, việc tăng số lượng dex2oat
(pm.dexopt.<reason>.concurrency
) có thể sử dụng lõi CPU tốt hơn nhằm
giúp đẩy nhanh tiến trình tổng thể của dexopt. Điều này đặc biệt hữu ích trong giai đoạn
khởi động.
Tuy nhiên, việc có quá nhiều lệnh gọi dex2oat có thể khiến thiết bị bị hết
bộ nhớ, mặc dù bạn có thể giảm thiểu điều này bằng cách đặt dalvik.vm.dex2oat-swap
thành
true
để cho phép sử dụng tệp hoán đổi. Quá nhiều lệnh gọi cũng có thể khiến
chuyển đổi ngữ cảnh không cần thiết. Do đó, bạn cần phải theo dõi cẩn thận số liệu này
trên cơ sở từng sản phẩm.
pm.dexopt.down_after_activate_days (mặc định: chưa đặt)
Nếu bạn đặt tùy chọn này, thì Dịch vụ ART chỉ loại bỏ các ứng dụng được sử dụng trong phạm vi cung cấp gần đây nhất số ngày.
Ngoài ra, nếu bộ nhớ gần hết, trong quá trình chuyển đổi dexopt ở chế độ nền, Dịch vụ ART
hạ cấp bộ lọc trình biên dịch của các ứng dụng không được dùng trong phạm vi ngày gần đây nhất
số ngày để giải phóng dung lượng. Lý do trình biên dịch cho thao tác này là inactive
,
và bộ lọc trình biên dịch được xác định bởi pm.dexopt.inactive
. Không gian
ngưỡng để kích hoạt tính năng này là ngưỡng dung lượng thấp của Trình quản lý bộ nhớ
(có thể định cấu hình thông qua cài đặt chung sys_storage_threshold_percentage
và
sys_storage_threshold_max_bytes
, mặc định: 500 MB) cộng với 500 MB.
Nếu bạn tuỳ chỉnh danh sách các gói thông qua
ArtManagerLocal#setBatchDexoptStartCallback
, các gói trong danh sách đã cung cấp
theo BatchDexoptStartCallback
cho bg-dexopt
sẽ không bao giờ được hạ cấp.
pm.dexopt.disable_bg_dexopt (mặc định: false)
Tính năng này chỉ dành cho mục đích thử nghiệm. Tính năng này ngăn Dịch vụ ART lên lịch ở chế độ nền công việc dexopt.
Nếu công việc dexopt ở chế độ nền đã được lên lịch nhưng chưa chạy, không có tác dụng. Tức là công việc sẽ vẫn chạy.
Một trình tự các lệnh được đề xuất để ngăn công việc dexopt ở chế độ nền đang chạy là:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Dòng đầu tiên ngăn việc lên lịch công việc dexopt ở chế độ nền nếu đó là chưa lên lịch. Dòng thứ hai huỷ lên lịch công việc dexopt ở chế độ nền, nếu việc đó đã được lên lịch và sẽ huỷ công việc dexopt ở chế độ nền ngay lập tức nếu nó đang chạy.
API dịch vụ ART
Dịch vụ ART cung cấp các API Java để tuỳ chỉnh. Các API này được định nghĩa trong
ArtManagerLocal
. Xem Javadoc trong
art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
cho
(nguồn Android 14, nguồn phát triển chưa phát hành).
ArtManagerLocal
là một singleton do LocalManagerRegistry
nắm giữ. Một người trợ giúp
hàm com.android.server.pm.DexOptHelper#getArtManagerLocal
giúp bạn
để có được nó.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
Hầu hết các API đều yêu cầu một thực thể của PackageManagerLocal.FilteredSnapshot
,
nơi lưu giữ thông tin của tất cả các ứng dụng. Bạn có thể nhận được bằng cách gọi điện
PackageManagerLocal#withFilteredSnapshot
, trong đó PackageManagerLocal
cũng nằm
một singleton do LocalManagerRegistry
nắm giữ và có thể lấy từ
com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal
.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
Sau đây là một số trường hợp sử dụng điển hình của các API.
Kích hoạt dexopt cho ứng dụng
Bạn có thể kích hoạt tính năng dexopt cho bất kỳ ứng dụng nào vào bất cứ lúc nào bằng cách gọi
ArtManagerLocal#dexoptPackage
.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
Bạn cũng có thể truyền lý do dexopt của riêng mình. Nếu bạn làm như vậy, lớp ưu tiên và bộ lọc trình biên dịch phải được đặt rõ ràng.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder("my-reason")
.setCompilerFilter("speed-profile")
.setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
.build());
}
Huỷ dexopt
Nếu một thao tác được khởi tạo bởi lệnh gọi dexoptPackage
, bạn có thể truyền một
tín hiệu huỷ, cho phép bạn huỷ hoạt động vào một thời điểm nào đó. Điều này có thể
hữu ích khi bạn chạy dexopt không đồng bộ.
Executor executor = ...; // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
cancellationSignal);
}
});
// When you want to cancel the operation.
cancellationSignal.cancel();
Bạn cũng có thể huỷ tính năng dexopt ở chế độ nền do Dịch vụ ART khởi tạo.
getArtManagerLocal().cancelBackgroundDexoptJob();
Nhận kết quả dexopt
Nếu một thao tác được khởi tạo bằng lệnh gọi dexoptPackage
, thì bạn có thể nhận được kết quả
khỏi giá trị trả về.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
Dịch vụ ART cũng tự bắt đầu các hoạt động dexopt trong nhiều trường hợp, chẳng hạn như
dexopt ở chế độ nền. Để nghe tất cả kết quả dexopt, cho dù thao tác có
do lệnh gọi dexoptPackage
hoặc do Dịch vụ ART khởi tạo, hãy sử dụng
ArtManagerLocal#addDexoptDoneCallback
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Đối số đầu tiên xác định xem có chỉ đưa nội dung cập nhật vào kết quả hay không. Nếu bạn chỉ muốn nghe các gói được cập nhật bằng dexopt, hãy đặt thành đúng.
Đối số thứ hai là trình thực thi của lệnh gọi lại. Để thực thi lệnh gọi lại trên
cùng một luồng thực hiện dexopt, hãy sử dụng Runnable::run
. Nếu bạn không muốn
lệnh gọi lại để chặn dexopt, sử dụng trình thực thi không đồng bộ.
Bạn có thể thêm nhiều lệnh gọi lại và Dịch vụ ART sẽ thực thi tất cả các lệnh gọi lại đó tuần tự. Tất cả lệnh gọi lại sẽ vẫn hoạt động cho mọi lệnh gọi trong tương lai trừ phi bạn xoá chúng.
Nếu bạn muốn loại bỏ lệnh gọi lại, hãy giữ lại tham chiếu của lệnh gọi lại khi bạn
thêm dữ liệu và sử dụng ArtManagerLocal#removeDexoptDoneCallback
.
DexoptDoneCallback callback = (result) -> {
// Process the result here.
...
};
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */, Runnable::run, callback);
// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);
Tuỳ chỉnh danh sách gói và các thông số dexopt
Dịch vụ ART bắt đầu các thao tác dexopt trong quá trình khởi động và ở chế độ nền
dexopt. Để tuỳ chỉnh danh sách gói hoặc các tham số dexopt cho các thao tác đó,
sử dụng ArtManagerLocal#setBatchDexoptStartCallback
.
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
switch (reason) {
case ReasonMapping.REASON_BG_DEXOPT:
var myPackages = new ArrayList<String>(defaultPackages);
myPackages.add(...);
myPackages.remove(...);
myPackages.sort(...);
builder.setPackages(myPackages);
break;
default:
// Ignore unknown reasons.
}
});
Bạn có thể thêm các mặt hàng vào danh sách gói, xoá các mặt hàng khỏi danh sách, sắp xếp các mặt hàng hoặc thậm chí hãy sử dụng một danh sách hoàn toàn khác.
Lệnh gọi lại của bạn phải bỏ qua các nguyên nhân không xác định vì có thể có nhiều lý do khác trong lệnh gọi lại tương lai.
Bạn có thể đặt tối đa một BatchDexoptStartCallback
. Lệnh gọi lại sẽ vẫn còn
hoạt động cho tất cả cuộc gọi trong tương lai trừ phi bạn xoá thông tin đó.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng
ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Tuỳ chỉnh các thông số của công việc dexopt ở chế độ nền
Theo mặc định, công việc dexopt ở chế độ nền sẽ chạy 1 lần/ngày khi thiết bị ở trạng thái rảnh
và đang sạc. Bạn có thể thay đổi chế độ này bằng cách sử dụng
ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Bạn có thể đặt tối đa một ScheduleBackgroundDexoptJobCallback
. Lệnh gọi lại sẽ
vẫn hoạt động cho tất cả cuộc gọi trong tương lai trừ phi bạn xoá.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng
ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
Tạm thời vô hiệu hoá dexopt
Mọi thao tác dexopt do Dịch vụ ART khởi tạo sẽ kích hoạt một
BatchDexoptStartCallback
. Bạn có thể tiếp tục huỷ các hoạt động để
vô hiệu hoá hiệu quả dexopt.
Nếu thao tác mà bạn huỷ là thao tác dexopt ở chế độ nền, thao tác đó sẽ tuân theo mặc định thử lại chính sách (30 giây, theo cấp số nhân, giới hạn trong 5 giờ).
// Good example.
var shouldDisableDexopt = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (shouldDisableDexopt.get()) {
cancellationSignal.cancel();
}
});
// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();
// Re-enable dexopt.
shouldDisableDexopt.set(false);
Bạn có thể có tối đa một BatchDexoptStartCallback
. Nếu bạn cũng muốn sử dụng
BatchDexoptStartCallback
để tuỳ chỉnh danh sách gói hoặc các tham số dexopt,
bạn phải kết hợp mã này vào một lệnh gọi lại.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
Thao tác dexopt được thực hiện khi cài đặt ứng dụng không do ART thực hiện
Dịch vụ. Thay vào đó, gói này do trình quản lý gói khởi tạo thông qua
Cuộc gọi dexoptPackage
. Do đó, hàm này không kích hoạt
BatchDexoptStartCallback
. Để tắt tính năng dexopt khi cài đặt ứng dụng, hãy ngăn
trình quản lý gói gọi dexoptPackage
.
Ghi đè bộ lọc trình biên dịch cho một số gói (Android 15 (thử nghiệm AOSP) trở lên
Bạn có thể ghi đè bộ lọc trình biên dịch cho một số gói nhất định bằng cách đăng ký
gọi lại thông qua setAdjustCompilerFilterCallback
. Lệnh gọi lại được gọi
bất cứ khi nào một gói được rút khỏi dex, bất kể quá trình dexopt được khởi tạo bằng
Dịch vụ ART trong khi khởi động và dexopt ở chế độ nền hoặc bằng lệnh gọi API dexoptPackage
.
Nếu một gói không cần điều chỉnh, lệnh gọi lại phải trả về
originalCompilerFilter
.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
Bạn chỉ có thể đặt một AdjustCompilerFilterCallback
. Nếu bạn muốn sử dụng
AdjustCompilerFilterCallback
để ghi đè bộ lọc trình biên dịch cho nhiều
bạn phải kết hợp mã này thành một lệnh gọi lại. Lệnh gọi lại vẫn còn
hoạt động cho tất cả cuộc gọi trong tương lai trừ phi bạn xoá thông tin đó.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng
ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Các chế độ tuỳ chỉnh khác
Dịch vụ ART cũng hỗ trợ một số tuỳ chỉnh khác.
Đặt ngưỡng nhiệt cho tính năng dexopt ở chế độ nền
Việc điều khiển nhiệt của công việc dexopt ở chế độ nền do Trình lập lịch biểu công việc thực hiện.
Công việc sẽ bị huỷ ngay lập tức khi nhiệt độ đạt đến
THERMAL_STATUS_MODERATE
. Ngưỡng
THERMAL_STATUS_MODERATE
có thể điều chỉnh được.
Xác định xem tính năng dexopt ở chế độ nền có đang chạy hay không
Công việc dexopt ở chế độ nền do Trình lập lịch công việc quản lý và mã công việc là
27873780
. Để xác định xem công việc có đang chạy hay không, hãy sử dụng các API Trình lập lịch biểu công việc.
// Good example.
var jobScheduler =
Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);
if (reason == PENDING_JOB_REASON_EXECUTING) {
// Do something when the job is running.
...
}
// Bad example.
var backgroundDexoptRunning = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(true);
}
});
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(false);
}
});
if (backgroundDexoptRunning.get()) {
// Do something when the job is running.
...
}
Cung cấp hồ sơ cho dexopt
Để sử dụng hồ sơ nhằm hướng dẫn dexopt, hãy đặt tệp .prof
hoặc tệp .dm
bên cạnh
APK.
Tệp .prof
phải là một tệp cấu hình định dạng nhị phân và tên tệp phải là
tên tệp của APK + .prof
. Ví dụ:
base.apk.prof
Tên tệp của tệp .dm
phải là tên tệp của APK có
phần mở rộng được thay thế bằng .dm
. Ví dụ:
base.dm
Để xác minh rằng hồ sơ đang được dùng cho dexopt, hãy chạy dexopt với
speed-profile
rồi kiểm tra kết quả.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
Dòng đầu tiên xoá tất cả hồ sơ do môi trường thời gian chạy tạo (tức là những hồ sơ trong
/data/misc/profiles
), nếu có, để đảm bảo rằng hồ sơ bên cạnh APK
hồ sơ duy nhất mà Dịch vụ ART có thể sử dụng. Dòng thứ hai chạy dexopt
bằng speed-profile
và truyền -v
để in kết quả chi tiết.
Nếu hồ sơ đang được sử dụng, bạn sẽ thấy actualCompilerFilter=speed-profile
trong
kết quả. Nếu không, bạn sẽ thấy actualCompilerFilter=verify
. Ví dụ:
DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}
Sau đây là những lý do điển hình khiến Dịch vụ ART không sử dụng hồ sơ này:
- Hồ sơ có tên tệp không chính xác hoặc không nằm bên cạnh tệp APK.
- Hồ sơ bị sai định dạng.
- Hồ sơ không khớp với APK. (Giá trị tổng kiểm trong hồ sơ không
khớp với giá trị tổng kiểm của các tệp
.dex
trong APK.)