إعدادات خدمة ART

قبل البدء، اطّلِع على نظرة عامة عالية المستوى على خدمة ART.

بدءًا من Android 14، تتولّى "خدمة ART" عملية تجميع AOT على الجهاز فقط للتطبيقات (المعروفة أيضًا باسم dexopt). "خدمة 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 (التلقائي: speed)

هذا هو فلتر المحول البرمجي الاحتياطي للتطبيقات التي تستخدمها تطبيقات أخرى.

من حيث المبدأ، تجري "خدمة 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 إلا للتطبيقات المستخدَمة خلال آخر عدد معيّن من الأيام.

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

إذا خصّصت قائمة الحِزم من خلال ArtManagerLocal#setBatchDexoptStartCallback، لن يتم أبدًا تخفيض الحِزم في القائمة التي يقدّمها by BatchDexoptStartCallback for 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;

تتطلب معظم واجهات برمجة التطبيقات مثيلاً من 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());
}

إلغاء عملية 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 فقط، اضبطها على "صحيح".

الوسيطة الثانية هي منفِّذ معاودة الاتصال. لتنفيذ معاودة الاتصال على سلسلة المحادثات نفسها التي تجري عملية 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();

لا تبدأ "خدمة ART" عملية dexopt التي يتم إجراؤها عند تثبيت التطبيق. بدلاً من ذلك، تبدأها "إدارة الحِزم" من خلال استدعاء dexoptPackage. لذلك، لا يتم تفعيل BatchDexoptStartCallback. لإيقاف عملية dexopt عند تثبيت التطبيق، امنع "إدارة الحِزم" من استدعاء dexoptPackage.

تجاوز فلتر المحول البرمجي لحِزم معيّنة (Android 15 والإصدارات الأحدث)

يمكنك تجاوز فلتر المحول البرمجي لحِزم معيّنة من خلال تسجيل معاودة اتصال من خلال setAdjustCompilerFilterCallback. يتم استدعاء معاودة الاتصال كلما كان سيتم إجراء عملية dexopt لحزمة، بغض النظر عمّا إذا كانت "خدمة وقت تشغيل Android ‏(ART)" قد بدأت عملية dexopt أثناء عملية التشغيل وعملية dexopt في الخلفية أو من خلال طلب بيانات من واجهة برمجة التطبيقات 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).