Показ дописів із міткою android. Показати всі дописи
Показ дописів із міткою android. Показати всі дописи

неділя, 7 квітня 2013 р.

Android Bitmap and OutOfMemoryError

In this post I am going to collect tips and tricks to help developer avoid OutOfMemoryError.

In code below I use this function to suspend current thread for some time:

public static void sleep(long time) {
  try {
    Thread.sleep(time);
  } catch (Exception e) {}
}

First of all let's talk about bitmap loading. Imagine you must load whole image with width 10x for imageview control with width x. Control don't need full size original image for good quality. Also if you load it as is then bitmap allocate too much memory. (If image configuration is ARGB_8888 then 4 bytes were allocated for every pixel). If we load scaled image with width x this will be enough. In first step we read only image size without loading content:
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(),     R.drawable.sample_image, options);
Now options variable contain width and height of image. I scale image by control width. BitmapFactory.Options have inSampleSize property. This property used to return smaller image to save memory. The documentation mentions using values that are a power of 2. Result code looks like this:

int scale = 1;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), resouceId, options);
int bmpWidth = options.outWidth;
if (bmpWidth > destWidth) {
  do {
    bmpWidth /= 2;
    if (bmpWidth > destWidth) {
      scale *= 2;
    }
  } while (bmpWidth > destWidth);
}
options.inJustDecodeBounds = false;
options.inSampleSize = scale;
Bitmap image = BitmapFactory.decodeResource(context.getResources(), resouceId, options);

Here we multiply scale variable by 2 and divide image width while it greater then destWidth. We get image with minimal size and good quality for display in control with destWidth width.

Let's consider a simple situation. We need to load bitmap, process it and destroy:

Bitmap image = ... // Load image
// Process image content
image.recycle();
sleep(100);
image = null;
System.gc();
sleep(100);
recycle function free up memoty associated with image. sleep function here take time for GC.


If you display large image in activity and need to free resources after activity was closed:
@Override
public void onCreate(Bundle savedInstanceState) {
  ...
  imageView = (ImageView) findViewById(R.id.full_image_view);
  imageView.setImageBitmap(...);
  ...
}

@Override
protected void onDestroy() {
  ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
  sleep(100);
  super.onDestroy();
  System.gc();
  sleep(100);
}

Upd: If image quality is not important to you:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
This value provide normal image quality and any pixel is stored in 2 bytes. If inPreferredConfig is null and decoder try to pick image with ARGB_8888 then one pixel is stored in 4 bytes in memory. For large images this will increase memory usage.

середа, 12 грудня 2012 р.

Android. Problem in database access from service

Few months ago I worked on my first android application. I use AlarmManager for work in background and call from it my intents receiver. Here the code:

public static final int FIRST_RUN = 5000; // 5 seconds
public static final int INTERVAL = 30000; // 30 sec
private AlarmManager alarmManager = null;
....
Intent intent = new Intent(this, FingerNotesServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
this.alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
this.alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + FIRST_RUN, INTERVAL,
pendingIntent);
FingerNotesServiceReceiver - this is my receiver class. It extends BroadcastReceiver class. Everything looks fine, but there are one problem. If you try to open connection to sqlite database file with write permissions from your receiver you will catch the error described below:

android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here


Sqlite don't allow you to create more than one write connections to db file. To prevent this in main process of application you can use static instance of SQLiteOpenHelper. But receiver still throws this error. This happens, because onReceive functions calls on separate process. When you try to gain access to database from receiver - application creates new static SQLiteOpenHelper object. Because existing object is staying in main process. To solve this I was add ContentProvider into application with needed functionality. Receiver interact with database through provider and code looks like I send queries to sqlite directly. As a result I got this scheme for application modules:




You can check my app on google play: FingerNotes