بهینهسازی برنامههای Android نیازمند دید عمیق به گردش کارهای همزمان و تعلیقهای کوروتین است.
اگرچه نمایهسازی محلی نمای کلی مفیدی ارائه میدهد، مقیاسبندی این اطلاعات آماری برای تولید بدون سربار همچنان یک چالش است.
این جلسه طرحی معماری برای حلقه بازخورد عملکرد مداوم ارائه میدهد.
androidx.tracing 2.0: پسزمینهها را بهطور پویا جابهجا کنید و زمینههای همروال (جریانهای Perfetto) را در رشتهها منتشر کنید.
ابزار دقیق هوشمند: از ردیابی ART محلی برای پیدا کردن نقاط داغ استفاده کنید، سپس تزریق نقاط ردیابی با سربار کم را خودکار کنید.
Macrobenchmarking: Track regressions and verify performance gains on physical devices in CI.
ردیابی میدانی: با ProfilingManager، فراتر از ردیابی محلی و CI بروید.
traceCoroutine ردیابی کنید یا نشانهای انتشار را به کد مبتنی بر فراخوانی برگشتی ارسال کنید.آوانویسی
Rahul Ravikumar
بعدازظهر همگی بهخیر.
نام من راهول است.
من مهندس تیم «تجربه توسعهدهنده نرمافزار Android» هستم.
من روی عملکرد برنامه و کتابخانه تمرکز میکنم و این صحبت درباره ردیابی است.
قبلاز اینکه عمیقاً به ردیابی بپردازیم، میخواهم چند واژه مشترک را تعریف کنم تا بتوانید بحث را دنبال کنید.
بنابراین هر زمان که در این صحبت به ردیابی اشاره میکنم، منظورم ردیابی صریح است، یعنی ابزارسازی دستی کد تا بدانید برنامه چه کاری انجام میدهد.
منظورم نمایهسازهای خودکار نیست که بعداً به آنها میپردازم.
بنابراین ردیابی دستی هنر بهجا گذاشتن ردپا در پایگاه کد شما است تا بتوانید بعداً وقتی مشکل عملکردی یا هر مشکل دیگری دارید، درباره آن استدلال کنید.
معمولاً خیلی بهتر از گزارشها است.
بنابراین، نمیدانم که خروجی Logcat یک برنامه بزرگ را دیدهاید یا نه.
معمولاً یک دیوار بزرگ از متن است که نمیتوانید بهراحتی درباره آن استدلال کنید.
ازسوی دیگر، ردیابی بسیار بهتر است.
تجسم کردن آن آسانتر است.
مدت زمان دارید.
ساختار دارید.
شما ابزارهایی دارید، بنابراین حتی میتوانید دادهها را از زوایای مختلف بررسی کنید و دقیقاً ببینید چه اتفاقی در چه زمانی افتاده است.
و بنابراین خیلی بهتر است.
و میتوانید، میدانید، عواملتان را به یک ردگیری هدایت کنید و بگویید، سؤال بپرسید.
میتوانید از ابزارهایی مانند Trace Processor، محکها، و حتی تبدیل ردگیریهایتان به سنجهها استفاده کنید.
بنابراین، اینها همه کارهایی هستند که میتوانید با ردگیریها انجام دهید و انجام آنها با Logcat دشوار است.
ازسوی دیگر، نمایهسازی، و من درباره نمایهسازی خودکار صحبت میکنم، معمولاً در پاسخ به مشکلی است که از آن اطلاع دارید.
بنابراین، برای مثال، ممکن است متوجه شده باشید که برنامهتان کند شروع میشود، یا چیزی نامنظم است، و میخواهید آن را محدود کنید، و میخواهید بفهمید چه اتفاقی دارد میافتد.
و در این مرحله، اگر ازقبل ردیابیهای دستی برای کمک به شما وجود نداشته باشد، اینجا جایی است که نمایهسازی خودکار وارد میشود.
ممکن است از چیزی مثل ردیابی روش ART استفاده کنید که در آن هر ورودی و خروجی روش را ابزاربندی میکنید.
یا ممکن است با ابزارهایی مانند Simpleperf به نمونهبرداری پشته نگاه کنید.
بنابراین، بهجای استفاده از هر ورودی و خروجی روش و داشتن سربار بالا، به نمونههای پشته فراخوانی نگاه میکنید و میبینید چه چیزی در مسیر داغ برنامهتان قرار دارد.
This is the year 2026, RAM is a problem everywhere, so you might be looking at memory, memory leaks and heap profiles.
بنابراین، این یک نمایهساز دیگر است که میتوانید از آن بهره ببرید.
میدانید، میتوانید به مسیرهای نگهداری انبوهه نگاه کنید.
میتوانید ببینید کدام کد درحال اختصاص دادن حافظه است و اینها همه انواع تجزیهوتحلیلی است که میتوانید با این ابزار انجام دهید.
راست.
اکنون آن واژگان را داریم.
پس حالا بیایید درباره اینکه چرا کتابخانه جدیدی ساختیم صحبت کنیم.
میاناهای برنامهسازی کاربردی ردیابی، میاناهای برنامهسازی کاربردی نسخه ۱.۰ در ابتدا در Android Jelly Bean معرفی شدند.
این اتفاق در سال ۲۰۱۲ افتاد.
این بیشاز ۱۴ سال پیش است.
از آن زمان، میاناهای برنامهسازی کاربردی آنطور که فکر میکنید تکامل نیافتهاند.
چند افزونه اضافه کردیم.
رویدادها و شمارندههای ناهمگام در Android 10 اضافه شدند.
این اتفاق در سال ۲۰۱۹ افتاد.
و همین بود.
از آن زمان، API تکامل نیافته است.
بااینحال، بومسازگان بسیار تکامل یافته است.
بنابراین، ردیابی پلاتفرم با جهشها و مرزها بهبود یافته است، اما هیچکدام از اینها ازنظر افزودن میانای برنامهسازی کاربردی به شما منتقل نشده است.
این چیزی است که میخواهیم تغییر دهیم.
مشکل بزرگ دیگر این است که ردیابی در نسخه ۱.۰ دارای یک پسای ثابت ردیابی است، به این معنی که هر زمان که یک بخش ردیابی را در کد خود اضافه میکنید، همیشه در میانگیر ردیابی سیستم قرار میگیرد.
Atrace زیرسیستم ردیابی Android است که مالک آن دادهها است.
بنابراین، اگر میخواستید ردیابیهایتان را بگیرید و آنها را به دورسنجی میدانی تبدیل کنید، این کار واقعاً بسیار سخت است، زیرا اکنون، الف، باید متوجه شوید که atrace چگونه آن دادهها را ذخیره میکند.
این قالب بهخوبی مستند شده است، اما مصرف آن ممکن است دشوار باشد.
بنابراین، این چیزی است که میخواهیم برطرف کنیم.
بنابراین، میخواهیم ابزارهایی را دراختیارتان قرار دهیم تا بتوانید دادههای ردیابیتان را به هرکجا که میخواهید ببرید.
همچنین در ردیابی نسخه ۱.۰ محدودیتی داشتیم که برچسبها به ۱۲۸ نویسه محدود میشدند.
خب، این احتمالاً در سال ۲۰۱۲ محدودیت معقولی بود، اما در سال ۲۰۲۶، این محدودیت کمی عجیب بهنظر میرسد.
آنچه وضعیت را بدتر میکند این است که نمیتوانید هیچ فراداده اضافهای به ردگیریتان اضافه کنید، که این مشکل را دوچندان میکند.
و سپس، هزینه سربار وجود دارد.
بنابراین، هرزمان که برنامهتان را ردیابی میکنید، باید کاملاً از سربار آگاه باشید، زیرا عمل مشاهده سیستم نباید آنچه را که سیستم در پشت صحنه انجام میدهد تغییر دهد.
بنابراین، اگر شرایط رقابتی وجود دارد که میخواهید بازتولید کنید، ردیابی نباید باعث ازبین رفتن شرایط رقابتی شود.
بنابراین، باید مطمئن شوید که همه معناشناسیهایی را که درغیراینصورت انجام میدهید حفظ میکنید.
Tracing 1.0 سربار نسبتاً بالایی داشت و میخواستیم آن را بهبود دهیم.
بسیارخوب.
من خیلی درباره مشکلات صحبت کردم.
اکنون، بیایید برخیاز مشکلات را بهصورت تصویری بررسی کنیم و ببینیم چگونه در عمل ترکیب میشوند.
بنابراین، در اینجا یک ردگیری Perfetto داریم.
برای برنامه com.example.app است.
ما یک رشته اصلی داریم و یک برش تکی بهنام initializeComponent داریم.
اکنون، در حالت ایدهآل، جزئیات بیشتری میخواهیم.
بنابراین، بیایید جزئیاتی اضافه کنیم.
بنابراین اکنون میدانیم که initializeComponent با performInitialize تماس میگیرد.
هنوز نمیدانیم کدام مؤلفه درحال مقداردهی اولیه است.
اکنون، تنها ابزار در کمربند ابزار ما با Tracing 1 نام است، بنابراین آن را به نام برش اضافه میکنیم.
اکنون، بهخاطر داشته باشید که نامهای برش ردیابی به ۱۲۸ نویسه محدود شدهاند، بنابراین در ردیابی شما کوتاه میشود.
اکنون، برای افزودن توهین به آسیب، سربار کوتاه کردن نیز اکنون سرباری است که هنگام ردیابی متحمل میشوید.
بنابراین، خیلی سریع با این مشکل مواجه میشوید.
بنابراین، این چیزی است که واقعاً میخواستیم اصلاح کنیم.
ردیابی Perfetto را دیدید و دیدید که ازنظر بصری چگونه است.
اکنون، بیایید از دیدگاه نوشتن کد برای انتشار آن بخشهای ردیابی نگاه کنیم.
دوباره، میانای برنامهسازی کاربردی initializeComponent را داریم.
بافت و نام را میگیرد، performInitialize را فراخوانی میکند.
تا اینجا که خوب بوده.
اکنون میخواهیم بااستفاده از Tracing 1.0 API یک ردیابی اضافه کنیم، بنابراین این کار را با وارد کردن androidx.tracing.trace و استفاده از بلوک ردیابی انجام میدهید.
نامی برای آن انتخاب میکنید.
اینجا performInitialize است.
اما همانطور که قبلاً میخواستیم انجام دهیم، میخواستیم نام مؤلفه را بدانیم، بنابراین از درونیابی رشته Kotlin استفاده میکنیم و نام را اضافه میکنیم.
و این اولین مشکل است.
بنابراین، در اینجا محدودیت ۱۲۸ نویسهای داریم.
Rahul Ravikumar
میتوانیم این مشکل را با Tracing 2 برطرف کنیم.
بهجای استفاده از androidx.tracing.trace، از Tracer استفاده میکنید.
این میانای برنامهسازی کاربردی بسیار شبیه است.
بهجای trace، Tracer.global.trace دارید.
دستهای دارید.
بعداً درباره دستهها با جزئیات بیشتری صحبت خواهیم کرد، اما اساساً شما یک نام دارید.
و سپس، هنوز بلوک کدی را که میخواهید ردیابی کنید دارید.
و اکنون دیگر محدودیت ۱۲۸ نویسهای ندارید.
درباره ردیابی سربار صحبت کردم.
میخواهم کمی بیشتر وارد جزئیات شوم و ببینم چطور این را بهبود دادهایم.
در Pixel 6a با Android 16، درج یک بخش شروع و پایان حدود ۱۲۰۰ نانوثانیه طول کشید.
شاید این مقدار زیاد بهنظر نرسد، اما اگر کد بسیار فعالی داشته باشید که بخشهای ردیابی زیادی اضافه میکند، این زمان از دست میرود و برنامهتان میتوانست آن را صرف رندر کردن فریمها کند.
بنابراین، نمیخواهیم این زمان را از شما بگیریم.
ما درواقع با کیت توسعه نرمافزار AndroidX Perfetto چیزهای زیادی را بهبود بخشیدیم.
این یک کیت توسعه نرمافزار آزمایشی بود که برای «ردیابی ترکیببندی» معرفی کردیم و درواقع وضعیت را بسیار بهبود بخشید.
اکنون، ما واقعاً هرگز نمیخواستیم شما این را ارسال کنید زیرا این Perfetto C SDK با اتصالات JNI است و C SDK حدود ۴ مگابایت در APK شما است.
این چیزی نیست که ما هرگز از شما خواسته باشیم ارسال کنید.
بنابراین، اکنون با Tracing 2، یک کتابخانه هدفمند داریم.
ما سربار را بسیار بیشتر بهبود دادهایم و اکنون خوشحال خواهید شد که بدانید Tracing 2 هفت برابر سریعتر است، به این معنی که زمان بیشتری برای انجام کارهایی که میخواستید انجام دهید دارید و میدانید که زیرساخت زمان کمتری میبرد.
چیزی مثل این چه چیزی را ممکن میکند که قبلاً ممکن نبود؟
بنابراین، یکی از نمونهها ویژگی ردیابی زمان اجرای Metro است.
بنابراین، Metro، برای کسانی که نمیدانند، یک کتابخانه تزریق وابستگی است.
از Kotlin چندپلاتفرمی پشتیبانی میکند.
این ابزار از افزونه کامپایلر Kotlin استفاده میکند.
این ابزار همه کدهای تزریق وابستگی را تولید میکند و با Tracing 2.0 ادغام میشود تا تجسم هزینه هر @Inject در کد شما را آسانتر کند.
بنابراین، این یک مثال است.
فرض کنید نمودار برنامه داریم.
در Metro، همه چیز به یک نمودار محدود میشود، بنابراین ما یک فیلد واحد داریم، AnalyticsTracker.
ما همچنین یک اتصال ارائهدهنده داریم.
ما یک کلاینت شبکه با OkHttpClient بهعنوان impl داریم، و سپس پیادهسازی AnalyticsTracker از AnalyticsTrackerImpl میآید، و ما به یک نمونه از کلاینت شبکه نیاز داریم که گراف میداند چگونه آن را ارائه دهد.
بنابراین، در پشت صحنه، افزونه کامپایلر کدی تولید میکند که شبیه به این است.
بنابراین، اگر AnalyticsTracker را داشته باشید، گیرنده نمونهای از کارخواه شبکه ایجاد میکند و آن را به سازنده AnalyticsTrackerImpl ارسال میکند که خوب است.
اما اکنون با فعال شدن ردیابی، میتوانید هزینه ایجاد را نه فقط برای پیادهسازی ردیاب، بلکه برای هر وابستگیای که نیاز دارد ببینید.
بنابراین اکنون این امکان را برای شما فراهم میکنیم که نهتنها هزینه نمونهسازی نوع را ببینید، بلکه همه وابستگیهای آن را نیز در ردیابی مشاهده کنید، و این واقعاً قدرتمند است.
بنابراین، Wise که یک برنامه مالی است بااستفاده از این اطلاعات، راهاندازی برنامهاش را تا ۴۵٪ بهبود بخشید.
آنها توانستند چیزهایی را که برای مقداردهی اولیه درطول راهاندازی برنامهشان ضروری نبود بهتعویق بیندازند، و همچنین برخیاز موارد با دامنه اشتباه را شناسایی کردند، جایی که چیزهایی که باید تکنمونه میبودند درواقع بهطور تصادفی تکنمونه نبودند، و اینها چیزهایی هستند که میتوانید در کد خود ببینید و بهراحتی برطرف کنید.
اگر به سنجههای راهاندازی آنها نگاه کنید، واقعاً چشمگیر هستند.
آنها نهتنها زمان لازم برای نمایش قاب اولیه را بهبود بخشیدند، بلکه زمان لازم برای نمایش کامل را نیز بهبود دادند.
شریک دیگری بهنام BandLab از همان اطلاعات در CI استفاده میکند.
بنابراین، نه تنها هزینه هر @Inject اتصال را میدانید، بلکه میدانید کدام رشته، آن هزینه را متحمل شده است.
بنابراین، وقتی این را بدانید، میتوانید همه اتصالهای گرانقیمت @Inject را در رشته اصلی پیدا کنید و در CI خود، میتوانید پُرسمانی را اجرا کنید تا بگویید، هی، فهرستی از همه اتصالهای گرانقیمت را به من بدهید و سپس آن را به تغییری که آنها را معرفی کرده است نسبت دهید و سپس میتوانید تغییر واقعی را مقصر بدانید و سعی کنید آن را برطرف کنید.
Rahul Ravikumar
بسیارخوب.
تا اینجا درباره همه چیزهایی که نسبت به Tracing 1 بهبود دادهایم صحبت کردهام، اما اکنون میخواهم درباره ویژگیهای جدیدی صحبت کنم که در Tracing 2 وجود دارد و قبلاً هرگز امکانپذیر نبود.
بنابراین، یکی از ویژگیهای اصلی «انتشار زمینه» نام دارد و چون برنامههای مدرن پیچیدهتر و پیچیدهتر میشوند، حجمهای کاری در چندین رشته، چندین روال همزمان، و چندین فرایند اجرا میشوند.
میخواهید بتوانید نحوه انتشار درخواست بین هر رشته درمقابل فرایند را تجسم کنید.
و این همان چیزی است که «انتشار بافت» امکانپذیر میکند.
بنابراین، اگر فقط فهرستی از برشها را به شما بدهم و چیزی درباره علت آنها نگویم، مفید هستند، اما استدلال درباره آنها چندان آسان نیست.
اما اگر بهجای آن به شما بگویم که این زنجیره علی رویدادهایی بود که اتفاق افتاد، آنگاه برای شما بسیار آسانتر میشود که بفهمید چه اتفاقی افتاده است و سپس هزینه هریک از بخشها را مرتبط کنید.
این همان چیزی است که «انتشار بافت» دردسترس قرار میدهد.
خوشحال خواهید شد اگر بدانید که «انتشار بافت» کاملاً پشتیبانی میشود و بنابراین، در اینجا نمونهای واقعی آورده شده است.
با نگاهی به ردگیری Perfetto com.example.app، فرض کنید performInitialize بیشازحد طول میکشد و میخواهید هزینه را بهتعویق بیندازید یا آن را به رشته دیگری منتقل کنید.
کاری که کاملاً میتوانید انجام دهید.
بنابراین، فرض کنید performInitialize را به رشته دیگری منتقل کردم.
اکنون، آن ماهیت سلسلهمراتبی برش را ازدست دادهاید.
بنابراین، اکنون دیگر نمیتوانید بگویید که initializeComponent باعث وقوع performInitialize شده است، مگراینکه به کد نگاه کنید.
در یک دنیای ایدهآل، شما میخواهید یک پیکان به شما بگوید که A باعث B شده است و سپس میتوانید درباره زنجیره علّی رویدادها استدلال کنید.
شاید حتی ردیابی سیستمی را دیده باشید که در آن ما واقعاً این کار را برای تراکنشهای binder در پلاتفرم انجام میدهیم.
بنابراین، در اینجا میتوانید ببینید که محک Compose تراکنش اتصالدهندهای به system_server انجام میدهد تا قاب را آماده کند.
بنابراین، اینها چیزهایی هستند که همیشه از دیدگاه ردیابی پلاتفرم امکانپذیر بودهاند، اما ما هرگز این ابزارها را دراختیار توسعهدهندگان نرمافزار قرار ندادهایم.
بنابراین اکنون، «انتشار بافت» کوروتین Kotlin بهطور کامل در Tracing 2.0 پشتیبانی میشود.
لازم نیست کاری انجام دهید.
فقط آن را از جعبه بیرون میآورید.
بنابراین، در اینجا یک روش تعلیق واحد به نام fetchProfile داریم.
client.get را فراخوانی میکند که روش تعلیق دیگری است و سپس برای غیرسریالی کردن پاسخ به Dispatchers.Default میرود.
کد کوروتین بسیار کلیشهای، چیزی که احتمالاً هر روز با آن سروکار داشتهاید.
اکنون، فرض کنید میخواهیم این را ردیابی کنیم.
بخش ردیابی اضافه میکنیم.
بنابراین در اینجا، بهجای trace، از میانای برنامهسازی کاربردی traceCoroutine استفاده میکنیم چون یک بلوک کد تعلیق است و سپس، برای فراخوانی داخلی قبلاز اینکه بافت را به Dispatchers.Default تغییر دهیم، بخش ردیابی داخلی دیگری را بااستفاده از همان میانای برنامهسازی کاربردی traceCoroutine اضافه میکنیم.
اکنون، در ردیابی، چیزی شبیه به این خواهید دید.
خواهید دید که کار ما در worker-1 شروع شد و سپس، درواقع تعویض بافت انجام شد و به worker-3 منتقل شد و پیکانهای مفیدی را در ردگیری خواهید دید که به آن اشاره میکنند.
نه تنها این، اگر روی مربع وسط بزرگنمایی کنید، میتوانید ببینید که deserialize درواقع در worker-1 شروع شده است و فراخوانی withContext باعث شده است که تعویض بافت اتفاق بیفتد و سپس، در worker-3 ادامه یافته است و سپس، fetchProfile در worker-3 ازسر گرفته شده است زیرا موتور روالهای همگام میخواهد تعداد تعویضهای بافت را که اتفاق میافتد به حداقل برساند.
میتوانید دقیقاً ببینید که یک کوروتین چه زمانی تعلیق شده است، چه زمانی ازسر گرفته شده است، و میتوانید زنجیره کامل علّی رویدادها را ببینید.
یکی دیگر از چیزهایی که Tracing 2 امکانپذیر میکند، توانایی افزودن فراداده سفارشی به ردگیری است.
بنابراین، در گذشته، تنها ابزاری که داشتید نام بخش ردیابی بود، اما اکنون همراه با آن، میتوانید یک بلوک فراداده اضافه کنید که به شما امکان میدهد هر تعداد ورودی فراداده که میخواهید اضافه کنید.
و میتوانید آن را به رد اضافه کنید.
وقتی به ردگیری واقعی نگاه میکنید، در اینجا میتوانید ببینید که همه فرادادههایی که اضافه کردهاید درواقع در ردگیری وجود دارد تا همه ابزارها بتوانند از آن استفاده کنند.
بنابراین اکنون میتوانید هرآنچه را میدانید، همه اطلاعات اضافهای را که همیشه میخواستید، جاسازی کنید.
بنابراین، من تعدادی ویژگی انتزاعی را به شما ارائه دادم که ردیابی امکانپذیر میکند، اما جایی که همه اینها میدرخشد، ویژگی جدیدی در Jetpack Compose به نام Recomposition Tracing است.
این ویژگی بهعنوان بخشی از Compose 1.13 عرضه شده است، بنابراین امروز برای استفاده شما آماده است.
بنابراین، این یک مثال است.
این «ردگیری ترکیببندی» است که احتمالاً همه شما با آن آشنا هستید.
این مورد از روزهای اولیه Compose وجود داشته است.
این ابزار هزینه نسبی یک composable و همه، میدانید، زیربخشهای آن را به شما میگوید، و هزینه هرکدام را در رشتهای که در آن رخ دادهاند نشان میدهد.
بنابراین، این کار بسیار ساده است.
چیزی که به شما نمیگوید بافت است.
این پیام به شما نمیگوید چرا این اتفاق افتاده است.
دلیل بازترکیب شدن موارد را به شما نمیگوید.
بنابراین، اگر بخواهم کوچکنمایی کنم، این همان منطقه موردعلاقهای است که در اسلاید قبلی به آن نگاه میکردیم.
و در سمت چپ، میتوانید ببینید که یک رویداد ورودی ارسال اتفاق افتاده است.
شاید این همان چیزی است که برخی از حالتها را تغییر داده است و شاید این همان چیزی است که باعث بازترکیب شده است.
اما نامشخص است.
مطمئن نیستید.
اما با ردیابی بازترکیب، اکنون فراداده سفارشی را به ردیابی اضافه میکنیم که دقیقاً به شما میگوید چرا بازترکیبها اتفاق افتادهاند.
نه تنها این، بلکه اکنون برای شما بسیار آسان است که بگویید این رویدادها مرتبط هستند.
و سپس اگر روی آنها کلیک کنید، دقیقاً به شما میگوید که چه وضعیتی تغییر کرده است.
به شما میگوید که این یک حالت بود، یک مقدار حالت تغییرپذیر بود که به نادرست تغییر کرد، که قبلاً درست بود.
نه فقط این، بلکه حتی پشته فراخوانی دقیقی را که باعث آن شده است به شما خواهیم گفت.
بنابراین، در اینجا میتوانید ببینید که AbstractClickableNode.performClick اتفاق افتاده است، که باعث تغییر حالت تغییرپذیر شده است، که باعث شده است بازترکیب اتفاق بیفتد.
بنابراین، این واقعاً، واقعاً جالب است، چیزی که قبلاً نمیتوانستیم انجام دهیم.
این ابزارها نیز دردسترس هستند، ببخشید، همه این عملکردها در ابزار جدید Android Performance Analyzer نیز دردسترس هستند، اگر میخواهید برخیاز ابزارهای عاملی را امتحان کنید.
و درست.
بنابراین، به شما گفتم که انتشار زمینه بهطور کامل برای کوروتینهای Kotlin پشتیبانی میشود.
اما درباره کدی که از ترکیبی از کدهای همروال درمقابل کدهای غیرهمروال استفاده میکند، یا شاید خودتان رشتهها را اجرا میکنید، یا شاید کد زمین قهوهای دارید که خیلی قدیمی است و به دلایلی از فراخوانهای برگشتی استفاده میکند، چه؟
بسیارخوب.
بنابراین، خبر خوب این است که هنوز میتوانید از تمام مزایای انتشار بافت در آنجا بهرهمند شوید.
بنابراین، فرض کنید یک API براونفیلد داریم که از یک callback و یک executor استفاده میکند.
پیادهسازی از executor.execute استفاده میکند، runCatching را فراخوانی میکند که جزئیات پیادهسازی است، پاسخ را دریافت میکند، و سپس پاسخ را به فراخوان برگشتی اطلاع میدهد.
بسیارخوب.
اکنون، اگر بخواهم این را ردیابی کنم، Tracer.global.trace را انجام میدهم، به آن برچسب میدهم، و سپس بلوک را احاطه میکنم.
اما با توجه به اینکه میخواهم زنجیره علّی رویدادها را نیز حفظ کنم، نوع اختیاری جدیدی را میپذیرم که به آن توکن انتشار میگویند.
و کاری که من انجام خواهم داد این است که فقط کد را به میانای برنامهسازی کاربردی ردیابی داخلی منتقل میکنم.
بنابراین، هر کسی که این روش را فراخوانی میکند، میتواند بهصورت اختیاری توکن انتشار را به من بدهد تا بتوانم زنجیره علّی رویدادهایی را که اتفاق میافتد حفظ کنم.
بسیارخوب.
اکنون، فراخواننده آن API میتواند یک API تعلیقکننده باشد.
بنابراین، در اینجا یک تابع تعلیق profileRequest دارم که اکنون قرار است میانای برنامهسازی کاربردی را که از یک بازخوانی و یک اجراکننده استفاده میکند فراخوانی کند.
در اینجا، چون دارم بین دو دنیا پل میزنم، یعنی دنیای تعلیق را به دنیای فراخوانی متصل میکنم، از سازنده suspendCoroutine استفاده خواهم کرد.
من یک ادامه دریافت خواهم کرد، و سپس فقط API را فراخوانی میکنم، و سپس ادامه را در فراخوانی برگشتی ازسر میگیرم، وقتی پاسخ را دریافت کردم.
و سپس در اینجا مجریای که تعریف میکنم profileRequestContext نام دارد.
این یک اجراکننده تکرشتهای است.
و چون traceCoroutine یک کد انتشار را در بافت روال همگام جاسازی میکند، میتوانم کد انتشار را از داخل بافت روال همگام دریافت کنم و سپس آن را بهعنوان ورودی به میانای برنامهسازی کاربردی براونفیلد ارسال کنم تا همچنان بتوانم زنجیره علّی رویدادها را حفظ کنم.
و در ردیابی، این موضوع را بسیار واضح میکند.
بنابراین، در اینجا چیزی دارید که در worker-3 شروع شده و به مجری تکرشتهای تغییر کرده است، وقتی که تعویض بافت را انجام دادیم، و سپس دوباره در worker ازسر گرفته شده است.
بنابراین خیلی جالب است.
Rahul Ravikumar
بسیارخوب.
یکی دیگر از کارهایی که میتوانید انجام دهید، سفارشیسازی Tracer.global است.
تاکنون، Tracing 2.0 API پیشفرضهای بسیار خوبی برای آنچه Tracer.global باید باشد به شما ارائه میدهد، اما اگر بخواهید آن را سفارشیسازی کنید، میتوانید.
همچنین، Tracing 2.0 از JVM و Android پشتیبانی میکند زیرا میخواهیم ردیابی بهطور کامل برای کتابخانههای Java و Android پشتیبانی شود.
بنابراین، سه مرحله برای مقداردهی اولیه عبارتند از: ایجاد یک سینک ردیابی.
سینک ردیابی درواقع نحوه ذخیره شدن بستههای ردیابی است.
این فایل قالب سیم و مکان ذخیره شدن ردیابیهای خروجی را تعریف میکند.
ما پیادهسازی آمادهای برای سینک ردگیری ارائه میدهیم که از قالب Perfetto پشتیبانی میکند، اما شما میتوانید سینک ردگیری خودتان را بنویسید.
این به تلاش برای حل این مشکل برمیگردد که atrace تنها پسزمینهای بود که در Tracing 1 دردسترس بود.
میخواستیم آن را برطرف کنیم.
بنابراین، اکنون اگر میخواهید یک سینک OpenTelemetry بنویسید یا میخواهید سینک خود را بیاورید، این کاملاً امکانپذیر است.
میتوانید انتخاب کنید که آن را لغو کنید.
وقتی سینک را داشتید، میتوانید درایور ردیابی ایجاد کنید و سینک را به آن بدهید.
و سپس میتوانید بهطور اختیاری انتخاب کنید که کدام دستهها را میخواهید، میدانید، روشن کنید.
بنابراین، این بخشی است که دستهها واقعاً میدرخشند.
بنابراین، میتوانید تصمیم بگیرید که در نسخه انتشار، برخی دستهها در مقایسه با نسخه اشکالزدایی مهمتر از بقیه هستند و میتوانید بگویید که اینها دستههایی هستند که میخواهید روشن کنید.
یا میتوانید بگویید: «میخواهم همه دستهها را بدون توجه به منبعشان روشن کنم.»
و این کاملاً طبیعی است.
همچنین میتوانید مشخصههای سفارشی را که برای ردیابی شما سراسری هستند اضافه کنید.
بنابراین میتوانید مواردی مانند نسخه ساخت، نوع، یا هر فراداده دیگری را که میخواهید به ردیابی پیوست کنید، بااستفاده از addAttribute API جاسازی کنید.
درنهایت، میتوانید با تماس با setGlobalTracer، ردیاب ثبت کنید.
این همان چیزی است که Tracer.global را به این نمونه ردیاب متصل میکند و درنتیجه، این همان چیزی است که برای فرایند دردسترس است.
در Android، چون فرایندها چرخه حیات بسیار بهتری دارند، تنها کاری که باید انجام دهید این است که AbstractTraceDriver.Factory را پیادهسازی کنید، روش create آن را لغو کنید، و دقیقاً همان کاری را انجام دهید که قبلاً انجام میدادید.
بنابراین شما سینک را ایجاد میکنید و سپس درایور را ایجاد میکنید.
لازم نیست اینجا ثبتنام کنید.
ما فقط ثبتنام را برای شما انجام میدهیم، چون میدانیم که این اتفاق اول افتاده است.
بسیارخوب.
بسیار خوب، میخواهم دوباره تأکید کنم که ردیابی برای چندپلاتفرمی آماده است.
این انتخاب کاملاً عمدی است زیرا میدانیم Compose در چندین پلاتفرم اجرا میشود و میخواهیم این امکان را برای شما فراهم کنیم که در بیشترین مکانهای ممکن ردیابی کنید.
درحالحاضر، از Android و JVM پشتیبانی میکنیم، اما پیادهسازی ما کاملاً باز است تا درصورت تمایل بتوانیم از پلاتفرمهای آینده پشتیبانی کنیم.
Rahul Ravikumar
خوشحال خواهید شد اگر بدانید R8، ابزاری که برای کوچک کردن و بهینهسازی کد منبع در Android استفاده میشود، از Tracing 2.0 استفاده میکند.
و با بهرهگیری از ردیابیها، R8 را بهینهسازی کردهایم تا کد شما را ۲۵٪ سریعتر کوچک و بهینه کند.
این یعنی زمان ساخت کوتاهتر و زمان انتظار کمتر برای مراحل بهینهسازی.
من همه بهینهسازیهایی را که در R8 انجام دادیم بررسی نمیکنم، اما میخواهم فقط یکی را بررسی کنم تا به شما نشان دهم که ردیابی چه چیزی را ممکن میسازد.
بنابراین، R8 در اصل یک مبدل منبع به منبع است.
بنابراین، شما یک دسته JAR دریافت میکنید، و آنها را کوچک و بهینهسازی میکنید، و مجموعه دیگری از JARها را تولید میکنید که میتوانید آنها را dex کنید.
بنابراین در اینجا، AGP با خواندن JARها شروع میکند که فقط برنامه شما نیست، بلکه همه وابستگیهای آن است.
بنابراین، از Gradle workers برای شروع خواندن همه JARهای ورودی استفاده میکند.
شما فایلهای JAR بسیار زیادی دارید که میخواهید بخوانید.
سپس، وقتی خواندن JARها تمام شد، R8 شروع به تجزیه کد میکند و مدلی از آنچه کد شما نشان میدهد و همه نقاط ورودی آن میسازد تا بتواند بهینهسازی و کوچک کردن کد شما را شروع کند.
اکنون، این همان بخش، await، است که نشان میدهد R8 واقعاً چه زمانی آماده است تا فرایند بهینهسازی را شروع کند، درست است؟
ممکن است فضای مردهای را در وسط ببینید و شاید برایتان سؤال باشد که چرا این زمان واحد پردازش مرکزی استفادهنشده را داریم.
And the reason is because the way that the old API worked was we waited for all the workers to have read all their files first before we parse anything.
واضح است که میتوانیم این مشکل را برطرف کنیم و دقیقاً همین کار را کردیم.
بنابراین در AGP 9، میتوانید ببینید که بهمحض اتمام خواندن JARهای ورودی، پردازش آنها را فوراً شروع کردیم.
و لازم نبود منتظر بمانیم تا تکتک کارگران خواندن ورودیهایشان را تمام کنند.
این کار درواقع باعث شد مدت زمانی که برای خواندن همه ورودیها منتظر میمانیم بسیار کاهش یابد.
بنابراین این یک بهینهسازی واضح است که وقتی به ردگیری نگاه میکنید، بسیار واضح است، اما وقتی فقط به کد منبع نگاه میکنید تا ببینید چه چیزی ارزش بهینهسازی دارد، چندان واضح نیست.
ابزار دیگری که ما هر روز بهعنوان مهندس AndroidX استفاده میکنیم Metalava است.
Metalava ابزاری است که از آن استفاده میکنیم تا مطمئن شویم هر میانای برنامهسازی کاربردی که عرضه میکنیم تضمینهای سازگاری میانای برنامهسازی کاربردی موردانتظار شما را دارد و کاملاً برای افراد خارجی قابلاستفاده است، بدون اینکه هیچگونه خطای ناخواستهای که میخواهیم از آن اجتناب کنیم وجود داشته باشد.
این ابزاری است که روی هر API عمومی و API محدودشده اجرا میشود، بنابراین هر زمانی که در اینجا صرفهجویی میکنیم درواقع زمانی است که بهعنوان مهندس AndroidX بهدست میآوریم.
Metalava همچنین از ردیابی برای بهبود زمانهای پساز ارسال به میزان قابلتوجه ۱۵٪ استفاده کرد.
و برای توضیح آنچه اتفاق افتاده است، به ردیابی میپردازیم، بنابراین Metalava دو نوع بررسی را اجرا میکند: اولی جایی است که APIهای محدودشده را بررسی میکند، و دومی جایی است که APIهای عمومی را بررسی میکند.
اما ممکن است متوجه الگویی شده باشید.
ما درحال انجام تنظیمات زیادی هستیم تا این بررسیها را دو بار اجرا کنیم.
بنابراین، اتفاقی که میافتد این است که همه ورودیهای برنامه خوانده میشوند، ما PSI را میسازیم که رابط ساختار برنامه است، یا، میدانید، دقیقاً شکل کد شما است که میتوان درباره آن استدلال کرد.
و سپس، AST را قبلاز شروع اعمال همه بررسیهای lint که میخواهیم بسازیم.
و ما این کار پرهزینه را دو بار انجام میدهیم.
چگونه این مشکل را برطرف کردیم؟
فقط یکبار این کار را کردیم.
بنابراین، بهجای اینکه همه آن کارهای راهاندازی را دو بار انجام دهیم، فقط یک بار آن را انجام دادیم و از آن ورودیها هم برای بررسیهای محدودشده و هم برای بررسیهای عمومی استفاده کردیم و درنتیجه در همه آن موارد صرفهجویی کردیم.
آره.
بسیارخوب.
Rahul Ravikumar
خب، حالا که تا حدودی به شما نشان دادم که Tracing 2.0 چه کارهایی را ممکن میکند، مهم است بدانید که هرچند ردیابی مهم است، دانستن اینکه چه چیزی را باید ردیابی کرد نیز به همان اندازه مهم است.
نمیخواهید ردپایتان را همهجا پخش کنید.
باید در انتخابهایتان بسیار دقت کنید و من سعی میکنم چند دستورالعمل درباره اینکه چه چیزی ارزش ردیابی دارد به شما ارائه دهم.
بنابراین، اولین کاری که باید انجام دهید این است که وقفههای توضیحدادهنشده را در ردیابیتان پیدا کنید.
بنابراین، هر زمان که رشتههای شما در حالت اجرا یا قابلاجرا هستند و شما وقفههای واضحی در ردگیری سیستم خود میبینید و نمیتوانید دلیلی برای آنها پیدا کنید و نمیدانید برنامهتان چه کاری انجام میدهد، ممکن است ارزش بررسی داشته باشد.
در آن مرحله، ممکن است یک گزارش ردیابی روشها را که با ردگیری سیستم همپوشانی دارد بگیرم و ببینم چه چیزی ارزش ردگیری دارد و ممکن است آن بخشهای ردگیری مربوطه را اضافه کنم.
همچنین میخواهید ازطریق مسیرهای کاربر اولویتبندی کنید.
بنابراین، میخواهید راهاندازی برنامه را بررسی کنید.
میخواهید به مسیرهای کاربر اصلی برنامه نگاه کنید و میخواهید با نگاه کردن به ردگیری، بفهمید که آیا میتوانید آنها را توضیح دهید یا نه.
بنابراین، هر زمان که نتوانید درباره چیزی استدلال کنید، آن چیز ارزش ردیابی دارد.
همچنین باید پشتههای فراخوانی عمیق یا استفاده غیرقابلتوضیح از CPU را ازطریق ردیابی بررسی کنید و سعی کنید دقیقاً بفهمید چه اتفاقی درحال رخ دادن است، و سپس، باید آن بخشها از پایگاه کد خود را به ردیابی تبدیل کنید.
بنابراین، میتوانید آنها را ردیابی کنید.
و سپس، هروقت رشتههایتان درحال خواب هستند، باید مطمئن شوید که میتوانید دلیل خواب بودن آنها را درک کنید.
آنها باید منتظر I/O باشند، یا باید منتظر تراکنش binder باشند، یا باید منتظر برخی رویدادهای ورودی خارجی باشند که انتظار دارید اتفاق بیفتد.
اما اگر، میدانید، خواب غیرقابل توضیحی داشته باشید، اینها بسیار شایسته بررسی هستند زیرا بدیهی است که ممکن است چیزی در آنجا پنهان باشد.
و سپس، وقتی که همه بخشهای ردیابی را در آن کد پخش کردید، باید آنها را به محک تبدیل کنید تا بتوانید تغییرات را در آنها ردیابی کنید.
بنابراین، با تکامل پایگاه کدتان، میتوانید ببینید که آیا سنجهها درحال تغییر هستند یا نه، و آیا داشبوردهایتان واقعاً تغییرات بزرگ را منعکس میکنند یا نه، زیرا در این صورت متوجه میشوید که چیز مهمی تغییر کرده است.
یا میتوانید از همه مراحل قبلی رد شوید و فقط از نمایندهتان بخواهید این کار را انجام دهد.
این دستورالعملها را بگیرید، آن را به یک مهارت تبدیل کنید، و سپس عامل دلخواهتان را به آن هدایت کنید، و سپس بگذارید آن را امتحان کند.
آره.
Rahul Ravikumar
بنابراین اگر شما را متقاعد کردهام که ردیابی تلاشی ارزشمند است و باید آن را انجام دهید، در اینجا راهاندازی پروژه را مشاهده میکنید.
خیلی ساده است.
ردیابی فقط یک کتابخانه ساده جاوا است، بنابراین شما وابستگی به androidx.tracing:tracing-wire را تعریف میکنید که ترکیبی از ردیابی و درایور ردیابی سازگار با Perfetto است، و نسخه پایدار فعلی ۲.۰.۵ است.
اگر کتابخانه Java هستید، دقیقاً همینطور است؛ باز هم، این یک میانای برنامهسازی کاربردی کاملاً چندپلاتفرمی است.
میخواهیم آن را به پلاتفرمهای دیگر نیز گسترش دهیم.
و بله، فقط میخواستم یک خلاصه سریع انجام دهم.
خب، درباره همه این ویژگیهای جدید و جالب که اضافه کردهایم صحبت کردم.
بنابراین «انتشار بافت» واقعاً بسیار قدرتمند است.
ما میتوانیم اطلاعات سفارشی، فراداده، اطلاعات پشته فراخوانی، و انواع چیزهای دیگر را جاسازی کنیم.
درباره خیلی چیزها صحبت نکردهام.
بدیهی است که میتوانید قالب ردیابی را تغییر دهید، میتوانید سینک ردیابی را تغییر دهید، میتوانید، میدانید، دادههای ردیابی خود را به OpenTelemetry ببرید.
اینها همه کارهایی است که میتوانید انجام دهید.
ردیابی چندپلاتفرمی است.
این هم تصمیمی بسیار بسیار آگاهانه است چون میخواهیم تا حد امکان افراد بیشتری بتوانند از این مزیت بهرهمند شوند.
و همه ابزارهایی که عرضه میکنیم، چه Perfetto باشد، چه AndroidX Benchmarks، چه تحلیلگر عملکرد جدید، یا Android Profiler قدیمی، همه از Tracing 2.0 پشتیبانی میکنند.
با این اوصاف، از اینکه گوش دادید بسیار سپاسگزاریم.
امیدوارم اجلاس توسعهدهندگان خوبی داشته باشید و از شما سپاسگزارم.