بهینه‌سازی تصاویر بیت‌مپ

اگر مراقب نباشید، کار با تصاویر می‌تواند به سرعت مشکلات عملکردی ایجاد کند. حتی یک تصویر گرافیکی کوچک با فرمت فشرده مانند JPG یا PNG می‌تواند هنگام رمزگشایی برای نمایش، به یک بیت‌مپ بزرگ تبدیل شود. اگر در نحوه استفاده از تصاویر کارآمد نباشید، ممکن است با مشکلات حافظه مواجه شوید که می‌تواند به عملکرد برنامه شما و سایر برنامه‌های روی دستگاه آسیب برساند. برای اطمینان از عملکرد بهینه برنامه خود، این بهترین شیوه‌ها را دنبال کنید.

استفاده از کتابخانه‌های بارگذاری تصویر

شما می‌توانید با استفاده از کتابخانه‌های بارگذاری تصویر مانند Coil (برای پروژه‌های Kotlin-first) یا Glide (برای پروژه‌های جاوا) کارایی برنامه خود را بهبود بخشید. این کتابخانه‌ها با انجام کارهایی مانند ذخیره تصاویر، کاهش حجم گرافیک در صورت نیاز و بازیافت اشیاء گرافیکی، میزان استفاده از حافظه برنامه شما را کاهش می‌دهند.

تصاویر نمونه‌برداری شده (Downsample)

مطمئن شوید که از اندازه تصویر مناسب برای نیازهای خود استفاده می‌کنید. از بارگذاری یک تصویر بزرگ و با وضوح بالا در یک ظرف کوچک (مانند یک تصویر بندانگشتی) خودداری کنید. در عوض، قبل از رمزگشایی تصویر در حافظه، از نمونه‌برداری کوچک برای کوچک کردن مقیاس آن استفاده کنید.

نمونه‌برداری کاهشی سمت کلاینت

کتابخانه‌های بارگذاری تصویر مانند Coil و Glide به طور خودکار نمونه‌برداری کاهشی را برای شما انجام می‌دهند. می‌توانید استراتژی‌های نمونه‌برداری کاهشی آنها را با استفاده از ImageLoader (برای Coil) یا DownsampleStrategy (برای Glide) پیکربندی کنید. اگر بیت‌مپ‌ها را به صورت دستی مدیریت می‌کنید، می‌توانید inSampleSize برای رمزگشایی نسخه کوچکتر استفاده کنید. برای انجام ایمن این کار، ابتدا باید inJustDecodeBounds روی true تنظیم کنید تا ابعاد تصویر را بدون تخصیص حافظه بخواند، اندازه نمونه را محاسبه کنید، inSampleSize را روی آن مقدار تنظیم کنید، inJustDecodeBounds را روی false تنظیم کنید و سپس تصویر را رمزگشایی کنید.

تغییر اندازه سمت سرور را ترجیح دهید

در صورت امکان، ابعاد دقیق تصویر مورد نیاز خود را مستقیماً از سرور backend خود درخواست کنید. این کار باعث کاهش استفاده از شبکه و فضای ذخیره‌سازی کش دیسک شما می‌شود، در حالی که با جلوگیری از سربار حافظه ناشی از تغییر اندازه تصاویر در دستگاه، استفاده از حافظه کمتری را فراهم می‌کند.

شما می‌توانید کتابخانه‌ها را طوری پیکربندی کنید که به صورت پویا اندازه نمای هدف را به URL تصویر اضافه کنند. برای مثال، Coil با استفاده از interceptor های سفارشی این امکان را فراهم می‌کند و Glide با استفاده از model loader های سفارشی (مانند BaseGlideUrlLoader ) از آن پشتیبانی می‌کند.

از اندازه‌های نامحدود طرح‌بندی خودداری کنید

برای اینکه لودرهای تصویر بتوانند به طور مؤثر (سمت کلاینت یا سمت سرور) نمونه‌برداری کاهشی انجام دهند، باید قبل از اجرای درخواست، اندازه هدف را بدانند.

از استفاده از wrapContentSize یا بدون محدودیت گذاشتن ابعاد در composableهایی که تصاویر از راه دور را بارگذاری می‌کنند، خودداری کنید. اگر این کتابخانه‌ها نتوانند مرزهای هدف را استنباط کنند، به بارگذاری تصویر با اندازه کامل اصلی برمی‌گردند. این می‌تواند منجر به بارگذاری تصویری بسیار بزرگتر از حد لازم شود که باعث افزایش استفاده از حافظه و تأخیر می‌شود.

در عوض، ابعاد صریحی را برای تصویر قابل ترکیب خود تنظیم کنید (برای مثال، با استفاده از Modifier.size ) یا نسبت ابعاد را تعریف کنید. این به موتور طرح‌بندی اجازه می‌دهد تا هدف پیکسل دقیق را از قبل محاسبه کند، که سپس بارگذار تصویر می‌تواند از آن برای درخواست و رمزگشایی محتوای با اندازه صحیح استفاده کند.

منابع جایگزین را برای اندازه‌های مختلف صفحه نمایش ارائه دهید

اگر تصاویر را با برنامه خود ارسال می‌کنید، ارائه دارایی‌هایی با اندازه‌های مختلف برای وضوح دستگاه‌های مختلف را در نظر بگیرید. این می‌تواند به کاهش حجم دانلود برنامه شما در دستگاه‌ها کمک کند و عملکرد را بهبود بخشد زیرا تصویری با وضوح پایین‌تر در دستگاهی با وضوح پایین‌تر بارگذاری می‌شود. برای اطلاعات بیشتر در مورد ارائه بیت‌مپ‌های جایگزین برای اندازه‌های مختلف دستگاه، به مستندات بیت‌مپ جایگزین مراجعه کنید .

مستقیماً پدگذاری نکنید

گاهی اوقات ممکن است نیاز به اضافه کردن فاصله بین عناصر تصویر (padding) داشته باشید. برای مثال، ممکن است بخواهید تصویر با یک حاشیه شفاف برای ایجاد کادر حروف احاطه شده باشد. در این مواقع، فاصله بین عناصر تصویر را مستقیماً به تصویر اضافه نکنید و ابعاد تصویر را تغییر ندهید. در عوض، ابعاد تصویر را همانطور که هستند رها کنید و با استفاده از InsetDrawable ، مکان تصویر را روی صفحه تنظیم کنید. به عنوان یک روش جایگزین، می‌توانید فاصله بین عناصر تصویر را در Composable یا View که تصویر را در خود نگه می‌دارد، اضافه کنید.

قالب پیکسلی مناسب را انتخاب کنید

با انتخاب فرمت پیکسل مناسب، بین حافظه و کیفیت تعادل برقرار کنید. وقتی به شفافیت نیاز ندارید RGB_565 استفاده کنید؛ این فرمت نصف فرمت پیش‌فرض ARGB_8888 از حافظه استفاده می‌کند.

در Glide می‌توانید این را با استفاده از DecodeFormat پیکربندی کنید. در Coil، می‌توانید از ویژگی bitmapConfig استفاده کنید.

در صورت امکان از بردارها استفاده کنید

برای تصاویری که از اشکال هندسی تشکیل شده‌اند، یک تصویر برداری (وکتور) بسیار کوچکتر از یک بیت‌مپ است و به طور روان برای هر تراکم نمایشی مقیاس‌بندی می‌شود. در صورت لزوم، از عناصری مانند ShapeDrawable برای نمایش گرافیک استفاده کنید.

هر زمان که می‌توانید، بیت‌مپ‌ها را منتشر و دوباره استفاده کنید.

فایل‌های گرافیکی بزرگ می‌توانند حافظه زیادی را اشغال کنند. برای کاهش تأثیر آنها، باید هر زمان که می‌توانید اشیاء گرافیکی را آزاد یا دوباره استفاده کنید.

اگر از یک کتابخانه بارگذاری تصویر استفاده می‌کنید، مطمئن شوید که وقتی دیگر به تصاویر بیت‌مپ نیاز ندارید، آنها را در مخزن مدیریت‌شده کتابخانه منتشر می‌کنید. کتابخانه می‌تواند در صورت نیاز از اشیاء دوباره استفاده کند و یک بافر حافظه برای نیازهای آینده در دسترس نگه می‌دارد.

اگر گرافیک‌ها را به صورت دستی مدیریت می‌کنید، باید پس از اتمام کار با بیت‌مپ‌ها، آنها را با فراخوانی Bitmap.recycle و حذف فوری ارجاع Bitmap ، آزاد کنید، به جای اینکه به جمع‌آوری زباله (garbage collection) تکیه کنید.

نکات و ترفندهای دیگر

این بخش چند روش دیگر برای بهبود عملکرد برنامه شما هنگام مدیریت گرافیک را فهرست می‌کند.

تصاویر بزرگ را با فایل AAB/APK خود بسته‌بندی نکنید

یکی از دلایل اصلی حجم بالای دانلود برنامه، گرافیک‌هایی است که درون فایل AAB یا APK قرار گرفته‌اند. از ابزار آنالیز APK استفاده کنید تا مطمئن شوید که فایل‌های تصویری بزرگتر از حد نیاز را در بسته‌بندی قرار نمی‌دهید. اندازه‌ها را کاهش دهید یا قرار دادن تصاویر روی یک سرور و دانلود آنها فقط در صورت نیاز را در نظر بگیرید.

پیدا کردن بیت‌مپ‌های اضافی

اگر چندین کپی از یک تصویر دارید، این باعث هدر رفتن حافظه می‌شود. می‌توانید از پروفایلر اندروید استودیو برای شناسایی گرافیک‌های اضافی استفاده کنید. از تحلیلگر heap dump برای گرفتن یک heap dump استفاده کنید و با انتخاب تنظیمات bitmaps تکراری ، نتایج را فیلتر کنید.

هنگام استفاده از ImageBitmap ، قبل از ترسیم، تابع prepareToDraw را فراخوانی کنید.

هنگام استفاده از ImageBitmap ، برای شروع فرآیند آپلود بافت به GPU، قبل از رسم واقعی آن، ImageBitmap#prepareToDraw() را فراخوانی کنید. این به GPU کمک می‌کند تا بافت را آماده کند و عملکرد نمایش تصویر روی صفحه را بهبود بخشد. اکثر کتابخانه‌های بارگذاری تصویر از قبل این بهینه‌سازی را انجام می‌دهند، اما اگر خودتان با کلاس ImageBitmap کار می‌کنید، باید این نکته را در نظر داشته باشید.

ترجیح می‌دهید به جای Painter یک Int DrawableRes یا URL را به عنوان پارامتر به composable خود ارسال کنید.

با توجه به پیچیدگی‌های کار با تصاویر (برای مثال، نوشتن یک تابع تساوی برای Bitmaps از نظر محاسباتی پرهزینه خواهد بود)، API Painter به صراحت با حاشیه‌نویسی @Stable به عنوان پایدار علامت‌گذاری نشده است. کلاس‌های ناپایدار می‌توانند منجر به ترکیب‌های غیرضروری شوند زیرا کامپایلر نمی‌تواند به راحتی تشخیص دهد که آیا داده‌ها تغییر کرده‌اند یا خیر.

بنابراین، توصیه می‌کنیم به جای ارسال Painter به عنوان پارامتر، یک URL یا شناسه منبع drawable را به عنوان پارامتر به composable خود ارسال کنید.

// Prefer this:
@Composable
fun MyImage(url: String) {

}
// Over this:
@Composable
fun MyImage(painter: Painter) {

}
{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}