Cấu hình dịch vụ ART

Trước khi bạn bắt đầu, hãy xem tổng quan cấp cao về Dịch vụ ART .

Kể từ Android 14, quá trình biên dịch AOT trên thiết bị cho các ứng dụng (còn gọi là dexopt) do ART Service xử lý. Dịch vụ ART là một phần của mô-đun ART và bạn có thể tùy chỉnh nó thông qua các thuộc tính hệ thống và API.

Thuộc tính hệ thống

Dịch vụ ART hỗ trợ tất cả các tùy chọn dex2oat có liên quan.

Ngoài ra, Dịch vụ ART hỗ trợ các thuộc tính hệ thống sau:

pm.dexopt.<reason>

Đây là tập hợp các thuộc tính hệ thống xác định các bộ lọc trình biên dịch mặc định cho tất cả các lý do biên dịch được xác định trước được mô tả trong các kịch bản Dexopt .

Để 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 tiêu 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 các ứng dụng được các ứng dụng khác sử dụng.

Về nguyên tắc, Dịch vụ ART thực hiện quá trình biên dịch theo hướng dẫn hồ sơ ( speed-profile ) cho tất cả các ứng dụng khi có thể, thường là trong quá trình dexopt nền. Tuy nhiên, có một số ứng dụng được các ứng dụng khác sử dụng (thông qua <uses-library> hoặc được tải độ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 hồ sơ cục bộ vì lý do riêng tư.

Đối với một ứng dụng như vậy, nếu yêu cầu biên dịch theo hướng dẫn hồ sơ, trước tiên, Dịch vụ ART sẽ cố gắng sử dụng hồ sơ đám mây. Nếu cấu hình đám mây không tồn tại, Dịch vụ ART sẽ quay lại sử dụng bộ lọc trình biên dịch được chỉ định bởi pm.dexopt.shared .

Nếu quá trình biên dịch được yêu cầu không được hướng dẫn theo hồ sơ thì thuộc tính này không có hiệu lực.

pm.dexopt.<reason>.concurrency (mặc định: 1)

Đây là số lượng lệnh gọi dex2oat vì một số lý do biên dịch được xác định trước ( first-boot , boot-after-ota , boot-after-mainline-updatebg-dexopt ).

Lưu ý rằng tác dụng của tùy chọn này được kết hợp với các tùy chọn sử dụng tài nguyên dex2oat ( dalvik.vm.*dex2oat-threads , dalvik.vm.*dex2oat-cpu-set và cấu hình tác vụ):

  • dalvik.vm.*dex2oat-threads kiểm soát số lượng luồng cho mỗi lệnh gọi dex2oat, trong khi pm.dexopt.<reason>.concurrency kiểm soát số lượng lệnh gọi dex2oat. Nghĩa là, số lượng luồng đồng thời tối đa là tích của hai thuộc tính hệ thống.
  • dalvik.vm.*dex2oat-cpu-set và các cấu hình tác vụ luôn giới hạn mức sử dụng lõi CPU, bất kể số lượng luồng đồng thời tối đa (đã thảo luận ở trên).

Một lệnh gọi dex2oat duy nhất có thể không sử dụng đầy đủ tất cả các lõi CPU, bất kể dalvik.vm.*dex2oat-threads . Do đó, việc tăng số lượng lệnh gọi dex2oat ( pm.dexopt.<reason>.concurrency ) có thể sử dụng lõi CPU tốt hơn, để tăng tốc tiến trình chung của dexopt. Điều này đặc biệt hữu ích trong quá trình khởi động.

Tuy nhiên, việc có quá nhiều lệnh gọi dex2oat có thể khiến thiết bị hết bộ nhớ, mặc dù điều này có thể được giảm thiểu 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ời gọi cũng có thể gây ra việc chuyển ngữ cảnh không cần thiết. Vì vậy, con số này cần được điều chỉnh cẩn thận trên cơ sở từng sản phẩm.

pm.dexopt.downgrade_after_inactive_days (mặc định: chưa được đặt)

Nếu tùy chọn này được đặt, Dịch vụ ART chỉ loại bỏ các ứng dụng được sử dụng trong số ngày nhất định gần đây nhất.

Ngoài ra, nếu bộ nhớ gần hết, trong quá trình dexopt ở chế độ nền, Dịch vụ ART sẽ hạ cấp bộ lọc trình biên dịch của các ứng dụng không được sử dụng trong số ngày nhất định gần nhất để giải phóng dung lượng. Lý do trình biên dịch cho điều này là inactive và bộ lọc trình biên dịch được xác định bởi pm.dexopt.inactive . Ngưỡng dung lượ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ý lưu trữ (có thể định cấu hình thông qua cài đặt chung sys_storage_threshold_percentagesys_storage_threshold_max_bytes , mặc định: 500MB) cộng thêm 500 MB.

Nếu bạn tùy chỉnh danh sách các gói thông qua ArtManagerLocal#setBatchDexoptStartCallback , các gói trong danh sách do BatchDexoptStartCallback cung cấp cho bg-dexopt sẽ không bao giờ bị hạ cấp.

pm.dexopt.disable_bg_dexopt (mặc định: sai)

Đây chỉ là để thử nghiệm. Nó ngăn Dịch vụ ART lên lịch công việc dexopt nền.

Nếu công việc dexopt nền đã được lên lịch nhưng chưa chạy thì tùy chọn này không có hiệu lực. Tức là công việc sẽ vẫn chạy.

Một chuỗi lệnh được đề xuất để ngăn công việc dexopt nền chạy là:

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

Dòng đầu tiên ngăn công việc dexopt nền được lên lịch nếu nó chưa được lên lịch. Dòng thứ hai hủy lịch trình công việc dexopt nền nếu nó đã được lên lịch và nó sẽ hủy công việc dexopt nền ngay lập tức nếu nó đang chạy.

API dịch vụ ART

Dịch vụ ART hiển thị các API Java để tùy chỉnh. Các API được xác định trong ArtManagerLocal . Xem Javadoc trong art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java để biết cách sử dụng ( nguồn Android 14 , nguồn phát triển chưa được phát hành ).

ArtManagerLocal là một đơn vị do LocalManagerRegistry nắm giữ. Chức năng trợ giúp 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 phiên bản PackageManagerLocal.FilteredSnapshot , chứa thông tin của tất cả các ứng dụng. Bạn có thể lấy nó bằng cách gọi PackageManagerLocal#withFilteredSnapshot , trong đó PackageManagerLocal cũng là một đơn vị do LocalManagerRegistry nắm giữ và có thể lấy được 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 API.

Kích hoạt dexopt cho một ứng dụng

Bạn có thể kích hoạt dexopt cho bất kỳ ứng dụng nào vào bất kỳ 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ể vượt qua lý do dexopt của riêng bạn. Nếu bạn làm điều đó, 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());
}

Hủy bỏ dexopt

Nếu một thao tác được bắt đầu bằng lệnh gọi dexoptPackage , bạn có thể chuyển tín hiệu hủy, tín hiệu này cho phép bạn hủy thao tác tại 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ể hủy dexopt 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 bắt đầu bằng lệnh gọi dexoptPackage , bạn có thể nhận được kết quả từ 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ự khởi tạo các hoạt động dexopt trong nhiều trường hợp, chẳng hạn như dexopt nền. Để nghe tất cả các kết quả dexopt, cho dù thao tác được bắt đầu bằng lệnh gọi dexoptPackage hay bởi Dịch vụ ART, 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 các 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 nó thành true.

Đối số thứ hai là người thực thi lệnh gọi lại. Để thực hiện 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, hãy 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 hiện tất cả các lệnh gọi lại một cách tuần tự. Tất cả các cuộc gọi lại sẽ vẫn hoạt động cho tất cả các cuộc gọi trong tương lai trừ khi bạn xóa chúng.

Nếu bạn muốn xóa một cuộc gọi lại, hãy giữ tham chiếu của cuộc gọi lại khi bạn thêm nó 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);

Tùy chỉnh danh sách gói và thông số dexopt

Dịch vụ ART tự khởi động các hoạt động dexopt trong quá trình dexopt khởi động và nền. Để tùy chỉnh danh sách gói hoặc tham số dexopt cho các thao tác đó, hãy 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ục vào danh sách gói, xóa các mục khỏi danh sách, sắp xếp hoặc thậm chí sử dụng một danh sách hoàn toàn khác.

Cuộc gọi lại của bạn phải bỏ qua các lý do không xác định vì nhiều lý do có thể được thêm vào trong tương lai.

Bạn có thể đặt tối đa một BatchDexoptStartCallback . Cuộc gọi lại sẽ vẫn hoạt động cho tất cả các cuộc gọi trong tương lai trừ khi bạn xóa nó.

Nếu bạn muốn xóa cuộc gọi lại, hãy sử dụng ArtManagerLocal#clearBatchDexoptStartCallback .

getArtManagerLocal().clearBatchDexoptStartCallback();

Tùy chỉnh các thông số của công việc dexopt nền

Theo mặc định, tác vụ dexopt nền sẽ chạy mỗi ngày một lần khi thiết bị ở chế độ rảnh và đang sạc. Điều này có thể được thay đổi 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 . Cuộc gọi lại sẽ vẫn hoạt động cho tất cả các cuộc gọi trong tương lai trừ khi bạn xóa nó.

Nếu bạn muốn xóa cuộc gọi lại, hãy sử dụng ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback .

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Tạm thời vô hiệu hóa dexopt

Bất kỳ thao tác dexopt nào được khởi tạo bởi Dịch vụ ART đều kích hoạt BatchDexoptStartCallback . Bạn có thể tiếp tục hủy các thao tác để vô hiệu hóa dexopt một cách hiệu quả.

Nếu thao tác bạn hủy là thao tác dexopt nền thì thao tác đó sẽ tuân theo chính sách thử lại mặc định (30 giây, hàm mũ, giới hạn ở mức 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ó nhiều nhất một BatchDexoptStartCallback . Nếu bạn cũng muốn sử dụng BatchDexoptStartCallback để tùy chỉnh danh sách gói hoặc tham số dexopt, bạn phải kết hợp mã thành một lệnh gọi lại.

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

Hoạt động dexopt được thực hiện khi cài đặt ứng dụng không được ART Service khởi tạo. Thay vào đó, nó được người quản lý gói khởi tạo thông qua lệnh gọi dexoptPackage . Do đó, nó không kích hoạt BatchDexoptStartCallback . Để tắt 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 nhất định (Android 15 (thử nghiệm AOSP)+)

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ý lệnh 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 sắp được giải mã, bất kể dexopt được khởi tạo bởi Dịch vụ ART trong quá trình khởi động và dexopt nền hay bằng lệnh gọi API dexoptPackage .

Nếu 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 muốn sử dụng AdjustCompilerFilterCallback để ghi đè bộ lọc trình biên dịch cho nhiều gói, bạn phải kết hợp mã vào một lệnh gọi lại. Cuộc gọi lại vẫn hoạt động cho tất cả các cuộc gọi trong tương lai trừ khi bạn xóa nó.

Nếu bạn muốn xóa cuộc gọi lại, hãy sử dụng ArtManagerLocal#clearAdjustCompilerFilterCallback .

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Các tùy chỉnh khác

ART Service còn hỗ trợ một số tùy chỉnh khác.

Đặt ngưỡng nhiệt cho dexopt nền

Việc kiểm soát nhiệt của công việc dexopt nền được thực hiện bởi Trình lập lịch công việc. Công việc bị hủy 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 dexopt nền có đang chạy không

Công việc dexopt nền được quản lý bởi Trình lập lịch công việc và ID công việc của nó là 27873780 . Để xác định xem công việc có đang chạy hay không, hãy sử dụng API Trình lập lịch 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 cấu hình để 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à tệp hồ sơ có đị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 cấu hình đang được sử dụng cho dexopt, hãy chạy dexopt với speed-profile và 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 xóa tất cả các cấu hình do thời gian chạy tạo ra (tức là các cấu hình trong /data/misc/profiles ), nếu có, để đảm bảo rằng cấu hình bên cạnh APK là cấu hình duy nhất mà ART Service có thể sử dụng. Dòng thứ hai chạy dexopt với speed-profile và nó chuyển -v để in kết quả dài dòng.

Nếu cấu hình đang được sử dụng, bạn sẽ thấy kết quả actualCompilerFilter=speed-profile . 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}

Những lý do điển hình khiến ART Service không sử dụng hồ sơ bao gồm:

  • Hồ sơ có tên tệp sai hoặc không nằm cạnh APK.
  • Hồ sơ có định dạng sai.
  • Hồ sơ không khớp với APK. (Tổng kiểm tra trong hồ sơ không khớp với tổng kiểm tra của tệp .dex trong APK.)