پیکربندی سرویس ART

قبل‌از شروع، نمای کلی سطح بالایی از سرویس ART را ببینید.

از Android 14 به بعد، گردآوری AOT درون‌دستگاهی برای برنامه‌ها (معروف به dexopt) توسط «سرویس ART» انجام می‌شود. «سرویس ART» بخشی از واحد ART است و می‌توانید آن را ازطریق دارایی‌های سیستم و «میاناهای برنامه‌سازی کاربردی» سفارشی‌سازی کنید.

خصوصیات سیستم

«سرویس ART» از همه گزینه‌های dex2oat مربوطه پشتیبانی می‌کند.

علاوه‌براین، «سرویس ART» از دارایی‌های سیستم زیر پشتیبانی می‌کند:

pm.dexopt.<reason>

این مجموعه خصوصیات سیستم است که فیلترهای پیش‌فرض گردآورنده را برای همه دلایل گردآوری ازپیش‌تعریف‌شده که در سناریوهای Dexopt توضیح داده شده است تعیین می‌کند.

برای اطلاعات بیشتر، فیلترهای گردآورنده را ببینید.

مقادیر پیش‌فرض استاندارد عبارت‌اند از:

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 (پیش‌فرض: سرعت)

این فیلتر کامپایلر جایگزین برای برنامه‌هایی است که برنامه‌های دیگر از آن‌ها استفاده می‌کنند.

در اصل، «سرویس ART» برای همه برنامه‌ها، درصورت امکان، معمولاً درطول dexopt پس‌زمینه‌ای، از گردآوری هدایت‌شده با نمایه (speed-profile) استفاده می‌کند. بااین‌حال، برخی‌از برنامه‌ها توسط برنامه‌های دیگر استفاده می‌شوند (یا ازطریق <uses-library> یا به‌صورت پویا بااستفاده از Context#createPackageContext با CONTEXT_INCLUDE_CODE بارگیری می‌شوند). این برنامه‌ها به‌دلایل حریم خصوصی نمی‌توانند از نمایه‌های محلی استفاده کنند.

برای چنین برنامه‌ای، اگر تدوین هدایت‌شده با نمایه درخواست شود، «سرویس ART» ابتدا سعی می‌کند از نمایه ابری استفاده کند. اگر نمایه ابری وجود نداشته باشد، «سرویس ART» به استفاده از فیلتر کامپایلر مشخص‌شده توسط pm.dexopt.shared برمی‌گردد.

اگر گردآوری درخواست‌شده هدایت‌شده با نمایه نباشد، این دارایی تأثیری ندارد.

pm.dexopt.<reason>.concurrency (پیش‌فرض: ۱)

این تعداد فراخوانی‌های dex2oat برای دلایل ازپیش تعریف‌شده خاصی از ترجمه است (first-boot، boot-after-ota، boot-after-mainline-update، و bg-dexopt).

توجه داشته باشید که تأثیر این گزینه با گزینه‌های استفاده از منابع dex2oat (dalvik.vm.*dex2oat-threads، dalvik.vm.*dex2oat-cpu-set، و نمایه‌های تکلیف) ترکیب می‌شود:

  • dalvik.vm.*dex2oat-threads تعداد رشته‌ها را برای هر فراخوانی dex2oat کنترل می‌کند، درحالی‌که pm.dexopt.<reason>.concurrency تعداد فراخوانی‌های dex2oat را کنترل می‌کند. یعنی حداکثر تعداد رشته‌های هم‌زمان برابر است با حاصل‌ضرب این دو ویژگی سیستم.
  • ‫dalvik.vm.*dex2oat-cpu-set و نمایه‌های تکلیف همیشه استفاده از هسته CPU را محدود می‌کنند، صرف‌نظر از حداکثر تعداد رشته‌های هم‌زمان (که در بالا بحث شد).

صرف‌نظر از dalvik.vm.*dex2oat-threads، یک فراخوانی dex2oat ممکن است به‌طور کامل از همه هسته‌های CPU استفاده نکند. بنابراین، افزایش تعداد فراخوانی‌های dex2oat (pm.dexopt.<reason>.concurrency) می‌تواند از هسته‌های CPU بهتر استفاده کند و پیشرفت کلی dexopt را تسریع کند. این امر به‌ویژه درطول راه‌اندازی مفید است.

بااین‌حال، داشتن تعداد زیادی فراخوانی dex2oat ممکن است باعث شود حافظه دستگاه تمام شود، هرچند این مشکل را می‌توان با تنظیم dalvik.vm.dex2oat-swap روی true کاهش داد تا امکان استفاده از فایل مبادله فراهم شود. فراخوانی‌های بیش‌ازحد نیز ممکن است باعث تعویض بافت غیرضروری شود. بنابراین، این عدد باید به‌دقت برای هر محصول تنظیم شود.

pm.dexopt.downgrade_after_inactive_days (پیش‌فرض: تنظیم نشده)

اگر این گزینه تنظیم شود، «سرویس ART» فقط برنامه‌هایی را که در تعداد روزهای داده‌شده اخیر استفاده شده‌اند dexopt می‌کند.

علاوه‌براین، اگر فضای ذخیره‌سازی تقریباً کم باشد، درطول dexopt پس‌زمینه، «سرویس ART» فیلتر کامپایلر برنامه‌هایی را که درطول تعداد روزهای مشخص‌شده اخیر استفاده نشده‌اند تنزل می‌دهد تا فضا آزاد شود. دلیل مترجم برای این کار inactive است، و فیلتر مترجم توسط pm.dexopt.inactive تعیین می‌شود. آستانه فضا برای راه‌اندازی این ویژگی، آستانه فضای کم «مدیر فضای ذخیره‌سازی» است (قابل پیکربندی ازطریق تنظیمات سراسری sys_storage_threshold_percentage و sys_storage_threshold_max_bytes، پیش‌فرض: ۵۰۰ مگابایت) به‌علاوه ۵۰۰ مگابایت.

اگر فهرست بسته‌ها را ازطریق ArtManagerLocal#setBatchDexoptStartCallback سفارشی‌سازی کنید، بسته‌های موجود در فهرست ارائه‌شده توسط BatchDexoptStartCallback برای bg-dexopt هرگز تنزل داده نمی‌شوند.

pm.dexopt.disable_bg_dexopt (پیش‌فرض: false)

این فقط برای آزمایش است. این کار مانع از آن می‌شود که «سرویس ART» کار dexopt پس‌زمینه را زمان‌بندی کند.

اگر کار dexopt پس‌زمینه ازقبل زمان‌بندی شده باشد اما هنوز اجرا نشده باشد، این گزینه تأثیری ندارد. یعنی کار همچنان اجرا خواهد شد.

توالی توصیه‌شده‌ای از فرمان‌ها برای جلوگیری از اجرای کار dexopt پس‌زمینه عبارت است از:

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

خط اول از زمان‌بندی شدن کار dexopt پس‌زمینه جلوگیری می‌کند، اگر هنوز زمان‌بندی نشده باشد. خط دوم زمان‌بندی کار dexopt پس‌زمینه را لغو می‌کند، اگر ازقبل زمان‌بندی شده باشد، و کار dexopt پس‌زمینه را فوراً لغو می‌کند، اگر درحال اجرا باشد.

میاناهای برنامه‌سازی کاربردی سرویس ART

«سرویس ART» میاناهای برنامه‌سازی کاربردی Java را برای سفارشی‌سازی دردسترس قرار می‌دهد. میاناهای برنامه‌سازی کاربردی در ArtManagerLocal تعریف شده‌اند. برای موارد استفاده، Javadoc را در art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java ببینید (منبع Android 14، منبع توسعه منتشرنشده).

‫ArtManagerLocal یک الگوی یگانه است که LocalManagerRegistry آن را نگه می‌دارد. تابع کمکی com.android.server.pm.DexOptHelper#getArtManagerLocal به شما کمک می‌کند آن را به‌دست آورید.

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

اکثر APIها به نمونه‌ای از PackageManagerLocal.FilteredSnapshot نیاز دارند که اطلاعات همه برنامه‌ها را دربرمی‌گیرد. می‌توانید آن را با فراخوانی PackageManagerLocal#withFilteredSnapshot دریافت کنید، که در آن PackageManagerLocal نیز یک سینگلتون است که توسط LocalManagerRegistry نگهداری می‌شود و می‌توان آن را از com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal دریافت کرد.

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

در زیر چند مورد از موارد استفاده معمول از این «میاناهای برنامه‌سازی کاربردی» آورده شده است.

راه‌اندازی dexopt برای برنامه

هرزمان بخواهید می‌توانید با فراخوانی ArtManagerLocal#dexoptPackage، dexopt را برای هر برنامه‌ای راه‌اندازی کنید.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

همچنین می‌توانید دلیل dexopt خودتان را ارسال کنید. اگر این کار را انجام دهید، کلاس اولویت و فیلتر کامپایلر باید به‌طور صریح تنظیم شود.

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());
}

لغو dexopt

اگر عملیاتی با تماس dexoptPackage شروع شود، می‌توانید سیگنال لغو ارسال کنید که به شما امکان می‌دهد عملیات را در نقطه‌ای لغو کنید. این کار می‌تواند وقتی dexopt را به‌صورت ناهمزمان اجرا می‌کنید مفید باشد.

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();

همچنین می‌توانید dexopt پس‌زمینه‌ای را که توسط «سرویس ART» آغاز شده است لغو کنید.

getArtManagerLocal().cancelBackgroundDexoptJob();

دریافت نتایج dexopt

اگر عملیاتی با فراخوانی dexoptPackage شروع شود، می‌توانید نتیجه را از مقدار برگشتی دریافت کنید.

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

«سرویس ART» همچنین در بسیاری از سناریوها، مانند dexopt پس‌زمینه، عملیات dexopt را خود آغاز می‌کند. برای شنیدن همه نتایج dexopt، چه عملیات با تماس dexoptPackage شروع شده باشد چه با «سرویس ART»، از ArtManagerLocal#addDexoptDoneCallback استفاده کنید.

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

اولین آرگومان تعیین می‌کند که آیا فقط به‌روزرسانی‌ها در نتیجه گنجانده شود یا نه. اگر فقط می‌خواهید به بسته‌هایی که dexopt به‌روزرسانی کرده است گوش دهید، آن را روی true تنظیم کنید.

دومین آرگومان اجراکننده تماس برگشتی است. برای اجرای برگشت تماس در همان رشته‌ای که dexopt را اجرا می‌کند، از Runnable::run استفاده کنید. اگر نمی‌خواهید بازخوانی dexopt را مسدود کند، از اجراکننده ناهم‌زمان استفاده کنید.

می‌توانید چند فراخوانی برگشتی اضافه کنید و «سرویس ART» همه آن‌ها را به‌صورت متوالی اجرا خواهد کرد. همه تماس‌های برگشتی برای همه تماس‌های آینده فعال باقی می‌مانند، مگراینکه آن‌ها را بردارید.

اگر می‌خواهید یک تماس برگشتی را بردارید، مرجع تماس برگشتی را هنگام افزودن آن نگه دارید و از 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);

سفارشی‌سازی فهرست بسته و پارامترهای dexopt

«سرویس ART» خودش عملیات dexopt را درطول راه‌اندازی و dexopt پس‌زمینه آغاز می‌کند. برای سفارشی‌سازی کردن فهرست بسته یا پارامترهای dexopt برای آن عملیات، از 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.
      }
    });

می‌توانید مواردی را به فهرست بسته اضافه کنید، مواردی را از آن حذف کنید، آن را مرتب کنید، یا حتی از فهرست کاملاً متفاوتی استفاده کنید.

کاربر باید دلایل ناشناخته را نادیده بگیرد زیرا ممکن است در آینده دلایل بیشتری اضافه شود.

حداکثر می‌توانید یک BatchDexoptStartCallback تنظیم کنید. تا زمانی که آن را پاک نکنید، تماس برگشتی برای همه تماس‌های آتی فعال خواهد ماند.

اگر می‌خواهید فراخوانی را پاک کنید، از ArtManagerLocal#clearBatchDexoptStartCallback استفاده کنید.

getArtManagerLocal().clearBatchDexoptStartCallback();

سفارشی‌سازی کردن پارامترهای کار dexopt پس‌زمینه‌ای

به‌طور پیش‌فرض، کار dexopt پس‌زمینه‌ای یک‌بار در روز وقتی دستگاه در حالت آماده‌به‌کار است و درحال شارژ است اجرا می‌شود. این تنظیم را می‌توان بااستفاده از ArtManagerLocal#setScheduleBackgroundDexoptJobCallback تغییر داد.

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

حداکثر می‌توانید یک ScheduleBackgroundDexoptJobCallback تنظیم کنید. این تماس برگشتی برای همه تماس‌های آینده فعال می‌ماند مگراینکه آن را پاک کنید.

اگر می‌خواهید تماس برگشتی را پاک کنید، از ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback استفاده کنید.

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

غیرفعال کردن موقت dexopt

هر عملیات dexopt که توسط «سرویس ART» آغاز شود باعث راه‌اندازی BatchDexoptStartCallback می‌شود. می‌توانید عملیات‌ها را لغو کنید تا عملاً dexopt را غیرفعال کنید.

اگر عملیاتی که لغو می‌کنید dexopt پس‌زمینه باشد، از خط‌مشی پیش‌فرض امتحان مجدد پیروی می‌کند (۳۰ ثانیه، نمایی، با سقف ۵ ساعت).

// 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);

حداکثر می‌توانید یک BatchDexoptStartCallback داشته باشید. اگر می‌خواهید از BatchDexoptStartCallback برای سفارشی‌سازی فهرست بسته یا پارامترهای dexopt نیز استفاده کنید، باید کد را در یک فراخوانی برگشتی ترکیب کنید.

// Bad example.

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

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

عملکرد dexopt که در نصب برنامه انجام می‌شود توسط ART Service آغاز نمی‌شود. درعوض، مدیر بسته ازطریق تماس dexoptPackage آن را آغاز می‌کند. بنابراین، BatchDexoptStartCallback را راه‌اندازی نمی‌کند. برای غیرفعال کردن dexopt در نصب برنامه، از مدیر بسته بخواهید dexoptPackage را فراخوانی نکند.

لغو فیلتر کامپایلر برای بسته‌های خاص (Android 15 و بالاتر)

با ثبت کردن بازخوانی ازطریق setAdjustCompilerFilterCallback، می‌توانید فیلتر کامپایلر را برای بسته‌های خاصی ملغی کنید. هرگاه بسته‌ای قرار باشد dexopt شود، این برگشت فراخوانی می‌شود، مهم نیست که dexopt توسط «سرویس ART» درطول بوت و dexopt پس‌زمینه‌ای یا توسط فراخوانی API dexoptPackage آغاز شده باشد.

اگر بسته‌ای نیاز به اصلاح ندارد، باید originalCompilerFilter را برگرداند.

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

فقط یک AdjustCompilerFilterCallback می‌توانید تنظیم کنید. اگر می‌خواهید از AdjustCompilerFilterCallback برای ملغی کردن فیلتر کامپایلر برای چند بسته استفاده کنید، باید کد را در یک برگشت ترکیب کنید. تا زمانی که آن را پاک نکنید، درخواست برگشت تماس برای همه تماس‌های آینده فعال می‌ماند.

اگر می‌خواهید تماس برگشتی را پاک کنید، از ArtManagerLocal#clearAdjustCompilerFilterCallback استفاده کنید.

getArtManagerLocal().clearAdjustCompilerFilterCallback();

سایر سفارشی‌سازی‌ها

«سرویس ART» از برخی‌از سفارشی‌سازی‌های دیگر نیز پشتیبانی می‌کند.

تنظیم آستانه حرارتی برای dexopt پس‌زمینه

کنترل حرارتی کار dexopt پس‌زمینه توسط «برنامه‌ریز کار» انجام می‌شود. وقتی دما به THERMAL_STATUS_MODERATE برسد، کار بلافاصله لغو می‌شود. آستانه THERMAL_STATUS_MODERATE قابل تنظیم است.

تعیین اینکه آیا dexopt پس‌زمینه‌ای درحال اجرا است یا نه

کار پس‌زمینه‌ای dexopt توسط «زمان‌بندی کار» مدیریت می‌شود و شناسه کار آن 27873780 است. برای تعیین اینکه آیا کار درحال اجرا است یا نه، از «میانای برنامه‌سازی کاربردی زمان‌بندی کار» استفاده کنید.

// 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.
  ...
}

نمایه‌ای برای dexopt ارائه دهید

برای استفاده از نمایه برای هدایت dexopt، فایل .prof یا فایل .dm را در کنار ‫APK قرار دهید.

فایل .prof باید فایل نمایه با قالب دودویی باشد و نام فایل باید نام فایل APK + .prof باشد. برای مثال،

base.apk.prof

نام فایل .dm باید نام فایل APK با پسوند جایگزین‌شده با .dm باشد. برای مثال،

base.dm

برای درستی‌سنجی اینکه از نمایه برای dexopt استفاده می‌شود، dexopt را با speed-profile اجرا کنید و نتیجه را بررسی کنید.

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

خط اول همه نمایه‌های تولیدشده توسط زمان اجرا (یعنی نمایه‌های موجود در /data/misc/profiles) را پاک می‌کند تا مطمئن شود که نمایه کنار APK تنها نمایه‌ای است که «سرویس ART» می‌تواند از آن استفاده کند. خط دوم dexopt را با speed-profile اجرا می‌کند، و -v را برای چاپ نتیجه پرحرف می‌گذراند.

اگر از نمایه استفاده شود، actualCompilerFilter=speed-profile را در نتیجه می‌بینید. درغیراین‌صورت، actualCompilerFilter=verify را می‌بینید. برای مثال،

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}

دلایل معمول برای اینکه «سرویس ART» از نمایه استفاده نمی‌کند شامل موارد زیر است:

  • نام فایل نمایه اشتباه است یا در کنار APK نیست.
  • نمایه در قالب اشتباه است.
  • نمایه با APK مطابقت ندارد. (جمع‌آزمای موجود در نمایه با جمع‌آزمای فایل‌های .dex در APK مطابقت ندارد.)