قبل از شروع، یک مرور کلی از سرویس ART را مشاهده کنید.
با شروع از اندروید ۱۴، کامپایل AOT روی دستگاه برای برنامهها (معروف به dexopt) توسط سرویس ART انجام میشود. سرویس ART بخشی از ماژول ART است و میتوانید آن را از طریق ویژگیهای سیستم و APIها سفارشی کنید.
ویژگیهای سیستم
سرویس 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 در صورت امکان، کامپایل مبتنی بر پروفایل ( speed-profile ) را برای همه برنامهها، معمولاً در حین dexopt پسزمینه، انجام میدهد. با این حال، برخی برنامهها وجود دارند که توسط برنامههای دیگر استفاده میشوند (یا از طریق <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و پروفایلهای وظیفه، صرف نظر از حداکثر تعداد رشتههای همزمان (که در بالا مورد بحث قرار گرفت)، همیشه میزان استفاده از هسته پردازنده را محدود میکردند.
صرف نظر از dalvik.vm.*dex2oat-threads ، یک فراخوانی dex2oat ممکن است به طور کامل از تمام هستههای CPU استفاده نکند. بنابراین، افزایش تعداد فراخوانیهای dex2oat ( pm.dexopt.<reason>.concurrency ) میتواند از هستههای CPU بهتر استفاده کند تا سرعت پیشرفت کلی dexopt را افزایش دهد. این امر به ویژه در هنگام بوت مفید است.
با این حال، تعداد زیاد فراخوانیهای dex2oat ممکن است باعث شود دستگاه با کمبود حافظه مواجه شود، اگرچه میتوان با تنظیم dalvik.vm.dex2oat-swap روی true برای استفاده از فایل swap، این مشکل را کاهش داد. همچنین، تعداد زیاد فراخوانیها ممکن است باعث تغییر غیرضروری زمینه شود. بنابراین، این تعداد باید به دقت و بر اساس هر محصول تنظیم شود.
pm.dexopt.downgrade_after_inactive_days (پیشفرض: تنظیم نشده)
اگر این گزینه تنظیم شود، سرویس ART فقط برنامههایی را که در تعداد روزهای مشخص آخر استفاده شدهاند، حذف میکند.
علاوه بر این، اگر فضای ذخیرهسازی تقریباً کم باشد، در طول 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 (پیشفرض: نادرست)
این فقط برای آزمایش است. این مانع از آن میشود که سرویس ART کار dexopt پسزمینه را زمانبندی کند.
اگر کار dexopt پسزمینه از قبل زمانبندی شده باشد اما هنوز اجرا نشده باشد، این گزینه هیچ تاثیری ندارد. یعنی، کار همچنان اجرا خواهد شد.
یک توالی از دستورات توصیه شده برای جلوگیری از اجرای کار dexopt در پسزمینه به شرح زیر است:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
خط اول از زمانبندی کار dexopt پسزمینه، اگر هنوز زمانبندی نشده باشد، جلوگیری میکند. خط دوم، اگر کار dexopt پسزمینه از قبل زمانبندی شده باشد، آن را از زمانبندی خارج میکند و اگر کار dexopt پسزمینه در حال اجرا باشد، آن را فوراً لغو میکند.
رابطهای برنامهنویسی کاربردی (API) خدمات ART
سرویس ART، APIهای جاوا را برای سفارشیسازی در اختیار قرار میدهد. این APIها در ArtManagerLocal تعریف شدهاند. برای اطلاع از موارد استفاده، به Javadoc در art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java مراجعه کنید ( منبع اندروید ۱۴ ، منبع توسعه منتشر نشده ).
ArtManagerLocal یک singleton است که توسط LocalManagerRegistry نگهداری میشود. یک تابع کمکی com.android.server.pm.DexOptHelper#getArtManagerLocal به شما در دریافت آن کمک میکند.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
اکثر APIها به یک نمونه از PackageManagerLocal.FilteredSnapshot نیاز دارند که اطلاعات همه برنامهها را در خود نگه میدارد. میتوانید آن را با فراخوانی PackageManagerLocal#withFilteredSnapshot دریافت کنید، که در آن PackageManagerLocal نیز یک singleton است که توسط LocalManagerRegistry نگهداری میشود و میتوان آن را از com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal دریافت کرد.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
در ادامه به برخی از کاربردهای معمول APIها اشاره شده است.
فعال کردن 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 تنظیم کنید.
آرگومان دوم، اجراکنندهی تابع فراخوانی (callback) است. برای اجرای تابع فراخوانی در همان نخی که dexopt را اجرا میکند، Runnable::run استفاده کنید. اگر نمیخواهید تابع فراخوانی، dexopt را مسدود کند، از یک اجراکنندهی ناهمزمان (asynchronous executor) استفاده کنید.
شما میتوانید چندین فراخوانی برگشتی اضافه کنید و سرویس ART همه آنها را به ترتیب اجرا خواهد کرد. همه فراخوانیهای برگشتی برای همه فراخوانیهای آینده فعال خواهند ماند، مگر اینکه آنها را حذف کنید.
اگر میخواهید یک تابع فراخوانی (callback) را حذف کنید، هنگام اضافه کردن آن، مرجع تابع فراخوانی را نگه دارید و 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 تنظیم کنید. این callback برای تمام فراخوانیهای آینده فعال خواهد ماند، مگر اینکه آن را پاک کنید.
اگر میخواهید callback را پاک کنید، ArtManagerLocal#clearBatchDexoptStartCallback استفاده کنید.
getArtManagerLocal().clearBatchDexoptStartCallback();
پارامترهای کار dexopt پسزمینه را سفارشی کنید
به طور پیشفرض، کار dexopt در پسزمینه، روزی یک بار، زمانی که دستگاه بیکار و در حال شارژ است، اجرا میشود. این مقدار را میتوان با استفاده از ArtManagerLocal#setScheduleBackgroundDexoptJobCallback تغییر داد.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
شما میتوانید حداکثر یک ScheduleBackgroundDexoptJobCallback تنظیم کنید. این callback برای تمام فراخوانیهای آینده فعال خواهد ماند، مگر اینکه آن را پاک کنید.
اگر میخواهید callback را پاک کنید، ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback استفاده کنید.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
دکسوپت را موقتاً غیرفعال کنید
هر عملیات dexopt که توسط سرویس ART آغاز شود، یک BatchDexoptStartCallback را فعال میکند. میتوانید عملیات را لغو کنید تا 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 نیز استفاده کنید، باید کد را در یک callback ترکیب کنید.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
عملیات dexopt که در نصب برنامه انجام میشود توسط سرویس ART آغاز نمیشود . در عوض، توسط مدیر بسته از طریق فراخوانی dexoptPackage آغاز میشود. بنابراین، BatchDexoptStartCallback را فعال نمیکند . برای غیرفعال کردن dexopt در نصب برنامه، از فراخوانی dexoptPackage توسط مدیر بسته جلوگیری کنید.
فیلتر کامپایلر را برای بستههای خاص نادیده بگیرید (اندروید ۱۵+)
شما میتوانید با ثبت یک callback از طریق setAdjustCompilerFilterCallback فیلتر کامپایلر را برای بستههای خاص نادیده بگیرید. این callback هر زمان که قرار است یک بسته حذف شود، فراخوانی میشود، فرقی نمیکند که dexopt توسط سرویس ART در طول بوت و حذف پسزمینه یا توسط یک فراخوانی API dexoptPackage آغاز شده باشد.
اگر یک پکیج نیازی به تنظیم نداشته باشد، تابع فراخوانی باید originalCompilerFilter برگرداند.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
شما فقط میتوانید یک AdjustCompilerFilterCallback تنظیم کنید. اگر میخواهید AdjustCompilerFilterCallback برای لغو فیلتر کامپایلر برای چندین بسته استفاده کنید، باید کد را در یک callback ترکیب کنید. callback برای همه فراخوانیهای بعدی فعال میماند، مگر اینکه آن را پاک کنید.
اگر میخواهید callback را پاک کنید، ArtManagerLocal#clearAdjustCompilerFilterCallback استفاده کنید.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
سایر سفارشیسازیها
سرویس ART همچنین از برخی سفارشیسازیهای دیگر پشتیبانی میکند.
آستانه حرارتی را برای دکسپت پس زمینه تنظیم کنید
کنترل حرارتی کار dexopt پسزمینه توسط زمانبند کار انجام میشود. کار بلافاصله پس از رسیدن دما به 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، یک فایل .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 مطابقت ندارند.)