بارگذاری نقشه های بیت بزرگ به طور موثر

توجه: چندین کتابخانه وجود دارد که بهترین شیوه ها را برای بارگذاری تصاویر دنبال می کنند. می توانید از این کتابخانه ها در برنامه خود برای بارگذاری تصاویر به بهینه ترین حالت استفاده کنید. ما کتابخانه Glide را توصیه می کنیم که تصاویر را تا حد امکان سریع و روان بارگیری و نمایش می دهد. دیگر کتابخانه‌های محبوب بارگیری تصویر عبارتند از Picasso from Square، Coil از Instacart و Fresco از Facebook. این کتابخانه ها بیشتر کارهای پیچیده مرتبط با بیت مپ و انواع دیگر تصاویر را در اندروید ساده می کنند.

تصاویر در همه اشکال و اندازه ها هستند. در بسیاری از موارد، آنها بزرگتر از حد مورد نیاز برای یک رابط کاربری نرم افزار معمولی (UI) هستند. به عنوان مثال، برنامه گالری سیستم عکس‌های گرفته شده با استفاده از دوربین دستگاه‌های اندرویدی شما را نمایش می‌دهد که معمولاً وضوح بسیار بالاتری نسبت به تراکم صفحه نمایش دستگاه شما دارند.

با توجه به اینکه با حافظه محدود کار می کنید، در حالت ایده آل فقط می خواهید نسخه ای با وضوح پایین تر را در حافظه بارگذاری کنید. نسخه با وضوح پایین تر باید با اندازه مؤلفه UI که آن را نمایش می دهد مطابقت داشته باشد. تصویری با وضوح بالاتر هیچ مزیت قابل مشاهده ای را ارائه نمی دهد، اما همچنان حافظه گرانبهایی را اشغال می کند و به دلیل مقیاس گذاری بیشتر در حین انجام کار، هزینه بیشتری را متحمل می شود.

این درس شما را در رمزگشایی بیت مپ های بزرگ بدون تجاوز از محدودیت حافظه هر برنامه با بارگذاری یک نسخه کوچکتر زیر نمونه در حافظه راهنمایی می کند.

ابعاد و نوع بیت مپ را بخوانید

کلاس BitmapFactory چندین روش رمزگشایی ( decodeByteArray() ، decodeFile() ، decodeResource() و غیره) را برای ایجاد یک Bitmap از منابع مختلف ارائه می دهد. مناسب ترین روش رمزگشایی را بر اساس منبع داده تصویر خود انتخاب کنید. این روش ها تلاش می کنند تا حافظه را برای بیت مپ ساخته شده تخصیص دهند و بنابراین به راحتی می توانند منجر به یک استثنا OutOfMemory شوند. هر نوع روش رمزگشایی دارای امضاهای اضافی است که به شما امکان می دهد گزینه های رمزگشایی را از طریق کلاس BitmapFactory.Options مشخص کنید. تنظیم ویژگی inJustDecodeBounds روی true در حین رمزگشایی، از تخصیص حافظه جلوگیری می کند، برای شی بیت مپ null برمی گرداند اما outWidth ، outHeight و outMimeType تنظیم می کند. این تکنیک به شما امکان می دهد ابعاد و نوع داده های تصویر را قبل از ساخت (و تخصیص حافظه) بیت مپ بخوانید.

کاتلین

val options = BitmapFactory.Options().apply {
    inJustDecodeBounds = true
}
BitmapFactory.decodeResource(resources, R.id.myimage, options)
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
val imageType: String = options.outMimeType

جاوا

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

برای جلوگیری از استثناهای java.lang.OutOfMemory ، قبل از رمزگشایی، ابعاد یک بیت مپ را بررسی کنید، مگر اینکه کاملاً به منبع اطمینان داشته باشید که داده های تصویر با اندازه قابل پیش بینی را در اختیار شما قرار می دهد که به راحتی در حافظه موجود قرار می گیرند.

یک نسخه کوچک شده را در حافظه بارگذاری کنید

اکنون که ابعاد تصویر مشخص شده است، می توان از آنها برای تصمیم گیری در مورد اینکه آیا تصویر کامل باید در حافظه بارگذاری شود یا به جای آن یک نسخه زیر نمونه بارگذاری شده استفاده کرد. در اینجا چند فاکتور برای در نظر گرفتن وجود دارد:

  • استفاده تخمینی از حافظه برای بارگذاری تصویر کامل در حافظه.
  • مقدار حافظه ای که می خواهید برای بارگیری این تصویر با توجه به سایر نیازهای حافظه برنامه خود متعهد شوید.
  • ابعاد ImageView یا مؤلفه رابط کاربری هدف که تصویر باید در آن بارگذاری شود.
  • اندازه و چگالی صفحه نمایش دستگاه فعلی.

برای مثال، ارزش بارگذاری یک تصویر 1024x768 پیکسل در حافظه را ندارد، اگر در نهایت در یک تصویر کوچک پیکسلی 128x96 در ImageView نمایش داده شود.

برای اینکه به رسیور بگویید که تصویر را نمونه برداری کند، با بارگذاری یک نسخه کوچکتر در حافظه، inSampleSize در شی BitmapFactory.Options خود روی true تنظیم کنید. به عنوان مثال، یک تصویر با وضوح 2048x1536 که با inSampleSize 4 رمزگشایی شده است، یک بیت مپ تقریباً 512x384 تولید می کند. بارگیری این در حافظه از 0.75 مگابایت به جای 12 مگابایت برای تصویر کامل استفاده می کند (با فرض پیکربندی بیت مپ ARGB_8888 ). در اینجا روشی برای محاسبه مقدار اندازه نمونه وجود دارد که توان دو بر اساس عرض و ارتفاع هدف است:

کاتلین

fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    // Raw height and width of image
    val (height: Int, width: Int) = options.run { outHeight to outWidth }
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {

        val halfHeight: Int = height / 2
        val halfWidth: Int = width / 2

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }

    return inSampleSize
}

جاوا

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

توجه: توان دو مقدار محاسبه می شود زیرا رمزگشا از یک مقدار نهایی با گرد کردن به نزدیکترین توان دو، مطابق مستندات inSampleSize ، استفاده می کند.

برای استفاده از این روش، ابتدا با inJustDecodeBounds که روی true تنظیم شده است رمزگشایی کنید، گزینه ها را عبور دهید و سپس با استفاده از مقدار جدید inSampleSize و inJustDecodeBounds روی false دوباره رمزگشایی کنید:

کاتلین

fun decodeSampledBitmapFromResource(
        res: Resources,
        resId: Int,
        reqWidth: Int,
        reqHeight: Int
): Bitmap {
    // First decode with inJustDecodeBounds=true to check dimensions
    return BitmapFactory.Options().run {
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(res, resId, this)

        // Calculate inSampleSize
        inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)

        // Decode bitmap with inSampleSize set
        inJustDecodeBounds = false

        BitmapFactory.decodeResource(res, resId, this)
    }
}

جاوا

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

این روش بارگذاری یک بیت مپ با اندازه دلخواه بزرگ را در ImageView که یک تصویر کوچک 100x100 پیکسل را نمایش می دهد، آسان می کند، همانطور که در کد مثال زیر نشان داده شده است:

کاتلین

imageView.setImageBitmap(
        decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100)
)

جاوا

imageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

می‌توانید با جایگزین کردن روش BitmapFactory.decode* مناسب، فرآیند مشابهی را برای رمزگشایی بیت‌مپ‌ها از منابع دیگر دنبال کنید.