تكوين خدمة ART

قبل أن تبدأ، راجع نظرة عامة رفيعة المستوى على خدمة ART .

بدءًا من Android 14، تتم معالجة تجميع AOT للتطبيقات (المعروف أيضًا باسم dexopt) على الجهاز بواسطة خدمة ART. تعد خدمة ART جزءًا من وحدة ART، ويمكنك تخصيصها من خلال خصائص النظام وواجهات برمجة التطبيقات.

خصائص النظام

تدعم خدمة ART جميع خيارات dex2oat ذات الصلة.

بالإضافة إلى ذلك، تدعم خدمة ART خصائص النظام التالية:

pm.dexopt.<السبب>

هذه مجموعة من خصائص النظام التي تحدد عوامل تصفية المترجم الافتراضية لجميع أسباب الترجمة المحددة مسبقًا الموضحة في سيناريوهات 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 بإجراء تجميع موجه للملف الشخصي ( speed-profile ) لجميع التطبيقات عندما يكون ذلك ممكنًا، عادةً أثناء عملية dexopt في الخلفية. ومع ذلك، هناك بعض التطبيقات التي تستخدمها تطبيقات أخرى (إما من خلال <uses-library> أو يتم تحميلها ديناميكيًا باستخدام Context#createPackageContext مع CONTEXT_INCLUDE_CODE ). لا يمكن لمثل هذه التطبيقات استخدام الملفات الشخصية المحلية لأسباب تتعلق بالخصوصية.

بالنسبة لمثل هذا التطبيق، إذا تم طلب التجميع الموجه بالملف الشخصي، تحاول خدمة ART أولاً استخدام ملف تعريف سحابي. في حالة عدم وجود ملف تعريف سحابي، تعود خدمة ART لاستخدام مرشح المترجم المحدد بواسطة pm.dexopt.shared .

إذا لم يكن التحويل البرمجي المطلوب موجهًا بملف التعريف، فلن يكون لهذه الخاصية أي تأثير.

pm.dexopt.<reason>.concurrency (الافتراضي: 1)

هذا هو عدد استدعاءات 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 وملفات تعريف المهام دائمًا ما تربط الاستخدام الأساسي لوحدة المعالجة المركزية، بغض النظر عن الحد الأقصى لعدد سلاسل العمليات المتزامنة (تمت مناقشته أعلاه).

قد لا يستخدم استدعاء dex2oat واحدًا جميع مراكز وحدة المعالجة المركزية بشكل كامل، بغض النظر عن dalvik.vm.*dex2oat-threads . لذلك، فإن زيادة عدد استدعاءات dex2oat ( pm.dexopt.<reason>.concurrency ) يمكن أن تستخدم نوى وحدة المعالجة المركزية بشكل أفضل، لتسريع التقدم الإجمالي لـ dexopt. وهذا مفيد بشكل خاص أثناء التمهيد.

ومع ذلك، قد يؤدي وجود عدد كبير جدًا من استدعاءات dex2oat إلى نفاد ذاكرة الجهاز، على الرغم من أنه يمكن تخفيف ذلك عن طريق ضبط dalvik.vm.dex2oat-swap على true للسماح باستخدام ملف المبادلة. قد يؤدي أيضًا عدد كبير جدًا من الاستدعاءات إلى تبديل السياق بشكل غير ضروري. ولذلك، ينبغي ضبط هذا الرقم بعناية على أساس كل منتج على حدة.

pm.dexopt.downgrade_after_inactive_days (الافتراضي: غير محدد)

إذا تم تعيين هذا الخيار، فإن خدمة ART تقوم فقط بإلغاء اختيار التطبيقات المستخدمة خلال آخر عدد محدد من الأيام.

بالإضافة إلى ذلك، إذا كانت مساحة التخزين منخفضة تقريبًا، أثناء عملية dexopt في الخلفية، تقوم خدمة ART بخفض مستوى مرشح المترجم للتطبيقات التي لم يتم استخدامها خلال آخر عدد محدد من الأيام، لتحرير المساحة. سبب المحول البرمجي لهذا inactive ، ويتم تحديد عامل تصفية المحول البرمجي بواسطة pm.dexopt.inactive . عتبة المساحة لتشغيل هذه الميزة هي عتبة المساحة المنخفضة لمدير التخزين (يمكن تكوينها من خلال الإعدادات العامة sys_storage_threshold_percentage و sys_storage_threshold_max_bytes ، الافتراضي: 500 ميجابايت) بالإضافة إلى 500 ميجابايت.

إذا قمت بتخصيص قائمة الحزم من خلال ArtManagerLocal#setBatchDexoptStartCallback ، فلن يتم تخفيض الحزم الموجودة في القائمة المقدمة من BatchDexoptStartCallback لـ bg-dexopt أبدًا.

pm.dexopt.disable_bg_dexopt (الافتراضي: خطأ)

هذا للاختبار فقط. يمنع خدمة 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;

تتطلب معظم واجهات برمجة التطبيقات مثيلاً لـ PackageManagerLocal.FilteredSnapshot ، الذي يحتفظ بمعلومات جميع التطبيقات. يمكنك الحصول عليه عن طريق الاتصال بـ PackageManagerLocal#withFilteredSnapshot ، حيث يعد PackageManagerLocal أيضًا رقمًا مفردًا يحتفظ به LocalManagerRegistry ويمكن الحصول عليه من com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal .

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

فيما يلي بعض حالات الاستخدام النموذجية لواجهات برمجة التطبيقات.

قم بتشغيل dexopt لأحد التطبيقات

يمكنك تشغيل dexopt لأي تطبيق في أي وقت عن طريق الاتصال بـ ArtManagerLocal#dexoptPackage .

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

إلغاء ديكسوبت

إذا تم بدء العملية عن طريق استدعاء 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();

الحصول على نتائج ديكسوبت

إذا تم بدء العملية بواسطة استدعاء 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 في الخلفية، فإنها تتبع سياسة إعادة المحاولة الافتراضية (30 ثانية، أسية، بحد أقصى 5 ساعات).

// 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. بدلاً من ذلك، يتم تشغيله بواسطة مدير الحزم من خلال استدعاء dexoptPackage . ولذلك، فإنه لا يؤدي إلى تشغيل BatchDexoptStartCallback . لتعطيل dexopt عند تثبيت التطبيق، امنع مدير الحزم من استدعاء dexoptPackage .

تجاوز مرشح المترجم لحزم معينة (Android 15 (AOSP التجريبي)+)

يمكنك تجاوز مرشح المترجم لحزم معينة عن طريق تسجيل رد اتصال من خلال setAdjustCompilerFilterCallback . يتم استدعاء رد الاتصال عندما يتم إلغاء اختيار الحزمة، بغض النظر عن بدء dexopt بواسطة خدمة ART أثناء التمهيد وdexopt في الخلفية أو عن طريق استدعاء dexoptPackage API.

إذا كانت الحزمة لا تحتاج إلى تعديل، فيجب أن يُرجع رد الاتصال 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 الخلفية بواسطة Job Scholer. يتم إلغاء المهمة على الفور عندما تصل درجة الحرارة إلى THERMAL_STATUS_MODERATE . عتبة THERMAL_STATUS_MODERATE قابلة للضبط.

تحديد ما إذا كان dexopt الخلفية قيد التشغيل

تتم إدارة وظيفة dexopt الخلفية بواسطة Job Scholer، ومعرف الوظيفة الخاص بها هو 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 Service لملف التعريف ما يلي:

  • يحتوي الملف الشخصي على اسم ملف خاطئ أو أنه ليس بجوار APK.
  • ملف التعريف بتنسيق خاطئ.
  • الملف الشخصي لا يتطابق مع APK. (لا تتطابق المجاميع الاختبارية الموجودة في الملف الشخصي مع المجاميع الاختبارية لملفات .dex في APK.)