بهینه سازی حافظه و پرفورمنس در برنامه نویسی اندروید


مدیریت حافظه و پرفورمنس در اندروید

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

اما چرا باید به چگونگی مصرف حافظه و پرفورمنس برنامه دقت کنیم؟

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

خصوصا در اندرویدهای نسخه 5.0 به پایین تا حد ممکن باید از رهاسازی حافظه با Garbage Collector جلوگیری نماییم. چرا که منجر به فریز شدن دیوایس می شود. فرضا اگر کاربر در حال اسکرول کردن یک لیست باشد. با حدود 200ms تاخیر در برنامه مواجه می شود.این مورد به هیچ وجه تجربه کاربری مناسبی رقم نمی زند.

از ساختن object های غیر ضروری در برنامه خودداری کنید

تا حد ممکن از object های ساخته شده استفاده مجدد نمایید و در قسمت هایی از برنامه که تخصیص object هزینه بر است دقت کنید. در غیر این صورت garbage collector بیشتر درگیر رهاسازی این object ها از حافظه می شود.

برای مثال از ساخت object در حلقه ها یا متدی مثل () onDraw در view اختصاصی که ساخته اید، خودداری کنید.

ساختمان داده ای متناسب با نیاز برنامه انتخاب کنید

اندروید پیاده سازی های متعددی برای کلاس Sparse*Array دارد. به خط زیر توجه کنید :

Map<Integer, String> map = new HashMap<Integer, String>();

با استفاده از این خط در کد در واقع object غیر ضروری Integer ساخته ایم. اما در مواردی نیاز به Data mapping داریم. در واقع data mapping حالتی ست که هر دو داده متفاوت را درون یک ساختمان داده دیگر قرار می دهیم تا بتوان هم زمان از آنها استفاده نمود.

اندروید برای چنین نیازی ساختمان داده های بهینه ای تعبیه کرده است. برای مثال در این حالت استفاده از HashMap انتخاب درستی ست. همان طور که گفته شد ساخت object هزینه بر است. باید تعداد دفعات صدا زده شدن garbage collector را حداقل نمود.

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

SparseArray<String> map = new SparseArray<String>();
map.put(1, "Hello");

مدیریت bitmap

bitmap ها می توانند حافظه زیادی را برای لود شدن کامل تصویر درگیر کند. فرض کنید در برنامه لازم باشد تصویری با ابعاد 100×100 dp با همین ابعاد لود کنیم.

یک راه رایج استفاده از قطعه کد زیر است :

// instruct BitmapFactory to only the bounds and type of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

// get width and height
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
// type of the image
String imageType = options.outMimeType;

اما با متد زیر می توان تصویری در scale مناسب با دیوایس نمایش داد:

public static Bitmap decodeBitmapWithGiveSizeFromResource(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);
}

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;
}

و به این ترتیب در کلاس مورد نظر از آن استفاده کنیم :

viewWidth = imageView.getWidth();
viewHeight = imageView.getHeight();

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

استفاده از حافظه پنهان یا cache

cache امکان استفاده مجدد از object هایی که ساختن دوباره آنها هزینه بر است را ممکن می سازد. برای مثال اگر در برنامه لازم باشد عکسی از اینترنت دانلود شود تا بارها در لیستی نمایش داده شود. باید از دانلود چندباره آن خودداری کنید. در واقع در مواردی لازم است تا object های ساخته شده، بازیابی شوند. در غیر این صورت، کم کم تمام حافظه درگیر خواهد شد.

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

قطعه کد زیر یک پیاده سازی ممکن برای LruCache  است.

public class ImageCache extends LruCache<String, Bitmap> {
 
  public ImageCache( int maxSize ) {
    super( maxSize );
  }
 
  @Override
  protected int sizeOf( String key, Bitmap value ) {
    return value.getByteCount();
  }
 
  @Override
  protected void entryRemoved( boolean evicted, String key, Bitmap oldValue, Bitmap newValue ) {
    oldValue.recycle();
  }
 
}

استفاده از آن ساده بوده و مانند کد زیر می باشد:

LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>();

برای مقداردهی اندازه cache بهتر است ابتدا میزان حافظه آزاد را محاسبه کرد. به این منظور می توان از MemoryClass استفاده کرد :

int memClass = ( ( ActivityManager) activity.getSystemService( Context.ACTIVITY_SERVICE ) ).getMemoryClass();
int cacheSize = 1024 * 1024 * memClass / 8;
LruCache cache = new LruCache<String, Bitmap>( cacheSize );

پاک نمودن cache برنامه

با override نمودن متد ()onTrimMemory در هنگام نیاز می توان برای عملیات foreground حافظه را آزاد کند.

 

 

به این پست امتیاز دهید

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

میانگین امتیاز 3 / 5. تعداد: 1

از امتیاز شما متشکریم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image