לפני שמתחילים, מומלץ לעיין בסקירה כללית ומקיפה של שירות ART.
החל מגרסה 14 של Android, שירות ART מטפל בתהליך הידור ה-AOT במכשיר לאפליקציות (שנקרא גם dexopt). שירות ART הוא חלק מהמודול של ART, וניתן להתאים אותו אישית באמצעות מאפייני מערכת וממשקי API.
מאפייני המערכת
שירות ART תומך בכל אפשרויות dex2oat.
בנוסף, ART Service תומך במאפייני המערכת הבאים:
pm.dexopt.<reason>
זוהי קבוצה של מאפייני מערכת שקובעים את מסנני ברירת המחדל של המהדר (compiler) מכל סיבות ההידור המוגדרות מראש שמתוארות בתרחישים של 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
ופרופילים של המשימות תמיד קשורים לליבת המעבד (CPU) בשימוש, ללא קשר למספר המקסימלי של שרשורים בו-זמנית (כפי שצוין שלמעלה).
יכול להיות שבהפעלה אחת של dex2oat לא ייעשה שימוש מלא בכל ליבות המעבד (CPU),
מתוך dalvik.vm.*dex2oat-threads
. לכן, הגדלת מספר ההפעלות של dex2oat (pm.dexopt.<reason>.concurrency
) יכולה לנצל טוב יותר את ליבות המעבד, כדי לזרז את ההתקדמות הכוללת של dexopt. האפשרות הזו שימושית במיוחד במהלך האתחול.
עם זאת, אם יהיו יותר מדי קריאות ל-dex2oat, יכול להיות שהמכשיר ייצא משימוש בגלל מחסור בזיכרון. אפשר לצמצם את הבעיה הזו על ידי הגדרת dalvik.vm.dex2oat-swap
לערך true
כדי לאפשר שימוש בקובץ החלפה. כמו כן, יותר מדי קריאות יכולות לגרום למעברים מיותרים של הקשר. לכן, צריך לכוונן את המספר הזה
בהתאם למוצר.
pm.dexopt.downgrad_after_inactive_days (ברירת מחדל: לא מוגדר)
אם האפשרות הזו מוגדרת, ART Service מבטל רק את האפליקציות שהשתמשו בהן עד הפעם האחרונה מספר ימים.
בנוסף, אם נפח האחסון נמוך, במהלך dexopt ברקע, שירות ART משדרג לאחור את מסנן המהדר של אפליקציות שלא נעשה בהן שימוש במספר הימים האחרון, כדי לפנות מקום. הסיבה לכך במהדר היא inactive
, והמסנן של המהדר נקבע לפי pm.dexopt.inactive
. הסף של נפח האחסון שגורם להפעלת התכונה הזו הוא הסף של נפח האחסון הנמוך ב-Storage Manager (ניתן להגדיר אותו דרך ההגדרות הגלובליות sys_storage_threshold_percentage
ו-sys_storage_threshold_max_bytes
, ברירת המחדל: 500MB) בתוספת 500MB.
אם תתאימו אישית את רשימת החבילות באמצעות ArtManagerLocal#setBatchDexoptStartCallback
, החבילות ברשימה ש-BatchDexoptStartCallback
מספק ל-bg-dexopt
לעולם לא יורדות לרמה נמוכה יותר.
pm.dexopt.disable_bg_dexopt (ברירת המחדל: false)
זהו קוד לבדיקה בלבד. הוא מונע מ-ART Service לתזמן את המשימה של dexopt ברקע.
אם משימת העיבוד ברקע כבר מתוזמנת אבל עדיין לא הופעלה, לאפשרות אין השפעה. כלומר, המשימה עדיין תפעל.
רצף הפקודות המומלץ למניעת הפעלת המשימה dexopt ברקע הוא:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
השורה הראשונה מונעת את תזמון המשימה dexopt ברקע, אם היא עדיין לא תוזמנה. בשורה השנייה מבטלים את התזמון של משימת dexopt ברקע, אם הוא כבר מתוזמן, והוא מבטל את עבודת דקספט ברקע באופן מיידי, אם פועלת.
ממשקי API של שירותי ART
שירות ART חושף ממשקי Java API לצורך התאמה אישית. ממשקי ה-API מוגדרים בקובץ ArtManagerLocal
. אפשר לעיין ב-Javadoc ב-art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
כדי לראות את השימושים (מקור ל-Android 14, מקור פיתוח שלא פורסם).
ArtManagerLocal
הוא אובייקט יחיד (singleton) שנמצא בבעלות של 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;
בהמשך מפורטים כמה תרחישים לדוגמה לשימוש בממשקי ה-API.
הפעלת dexopt לאפליקציה
אפשר להפעיל dexopt לכל אפליקציה בכל שלב באמצעות התקשרות
ArtManagerLocal#dexoptPackage
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
אתם יכולים גם להעביר את הסיבה שלכם לבדיקה. אם תעשו זאת, סיווג העדיפות יש להגדיר את מסנן המהדר (compiler) באופן מפורש.
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();
אפשר גם לבטל את הסרת הנתונים ברקע ביוזמת שירות ART.
getArtManagerLocal().cancelBackgroundDexoptJob();
קבלת תוצאות של dexopt
אם הפעולה מופעלת על ידי קריאה ל-dexoptPackage
, אפשר לקבל את התוצאה מהערך המוחזר.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
שירות ART גם יוזם פעולות dexopt בעצמו בתרחישים רבים, כמו
בדיקת רקע ברקע. כדי להאזין לכל התוצאות של dexopt, בין שהפעולה מופעלת על ידי קריאה ל-dexoptPackage
ובין שהיא מופעלת על ידי שירות ART, משתמשים ב-ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
הארגומנט הראשון קובע אם לכלול רק עדכונים בתוצאה. אם רוצים להאזין רק לחבילות שמתעדכנות על ידי dexopt, צריך להגדיר את הערך כ-true.
הארגומנט השני הוא מבצע הקריאה החוזרת (callback). כדי להריץ את פונקציית ה-callback באותו חוט שבו מתבצעת ה-dexopt, משתמשים ב-Runnable::run
. אם אתם לא רוצים שהקריאה החוזרת (callback) תחסום את 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
אחד לכל היותר. הקריאה החוזרת (callback)
יישארו פעילים בכל השיחות העתידיות, אלא אם תמחקו את ההגדרה.
כדי לבטל את הקריאה החוזרת, צריך להשתמש ב-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 ואילך)
אפשר לשנות את ברירת המחדל של מסנן המהדר עבור חבילות מסוימות על ידי רישום של פונקציית קריאה חוזרת (callback) דרך setAdjustCompilerFilterCallback
. הקריאה החוזרת היא
בכל פעם שחבילה עומדת לעבור פירוק, בלי קשר לדקסופ ביוזמת
שירות ART במהלך הפעלה והסרה ברקע, או באמצעות קריאה ל-API של dexoptPackage
.
אם לא צריך לבצע התאמה בחבילה, צריך להחזיר את הקריאה החוזרת
originalCompilerFilter
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
אפשר להגדיר רק AdjustCompilerFilterCallback
אחד. אם רוצים להשתמש ב-AdjustCompilerFilterCallback
כדי לשנות את ברירת המחדל של מסנן המהדר לכמה חבילות, צריך לשלב את הקוד בקריאה חוזרת אחת. הקריאה החוזרת (callback) נשארה
פעיל לכל השיחות העתידיות, אלא אם תמחקו את ההגדרה.
כדי לבטל את הקריאה החוזרת, משתמשים ב-ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
התאמות אישיות נוספות
שירות ART תומך גם בכמה התאמות אישיות אחרות.
הגדרת הסף התרמי ל-dexopt ברקע
בקרת התרמית של משימת dexopt ברקע מתבצעת על ידי Job Scheduler.
המשימה תבוטל מיידית כשהטמפרטורה תגיע לערך THERMAL_STATUS_MODERATE
. הסף של
ניתן לכוונן את THERMAL_STATUS_MODERATE
.
איך בודקים אם תהליך dexopt פועל ברקע
המשימות של dexopt ברקע מנוהלות על ידי Job Scheduler, ומזהה המשימה שלהן הוא 27873780
. כדי לבדוק אם המשימה פועלת, משתמשים בממשקי ה-API של Job Scheduler.
// 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).