java – What is the best way to store application images in drawable or assets

Question:

The application has a complex design and therefore it was decided to draw on the surfaceView, that is, there are almost no xml files, everything is rendered programmatically. But there is a fairly large number of small png files, of different sizes. Tell me what is the best way to store all these files? options that I am considering: drawable – store in a shared folder and resize them programmatically, assets – everything is the same and there is a third option – to load all images as a texture atlas in one file and then create bitmaps over the areas of a large file. Please describe the pros and cons of each option, maybe there are some other options that you encountered? (Downloading via the Internet is not suitable).

Answer:

The asset directory is fundamentally different from drawable in that drawable allows drawable to store different images for different resolutions, orientations and locales. asset other asset , allows you to organize storage in the form of files.

For your case, I suppose, drawable still more preferable, since you can organize access through image identifiers, while in the case of asset you will have to organize access through a kind of file system, with reading, organizing a stream, etc., like this:

public void loadImageFromAsset() {
    try {
        InputStream ims = getAssets().open("cat.jpg");
        Drawable d = Drawable.createFromStream(ims, null);
        mImage.setImageDrawable(d);
    }
    catch(IOException ex) {
        return;
    }
}

Now, the question about "cutting / not cutting" on pictures for different dpi , fundamentally it differs only in that if you do not "cut" pictures, then you either prick OutOfMemoryException on large pictures, or programmatically change the resolution of the picture through InSampleSize (picture scaling):

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

That is, output like this:

// сначала проверяете реальный размер битмапа
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

//далее вычисляем inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// теперь декодируем с правильным масштабированием    
options.inJustDecodeBounds = false;
Bitmap bimtap=BitmapFactory.decodeResource(res, resId, options);
//отображаем
imageView.setImageBitmap(bitmap);

More details here

So in the case when the pictures are stored in separate drawable-??dpi – all these calculations are done in advance. This saves time for scaling pictures.

It is up to you to decide whether you want to scale up in advance or whether you agree to kill some of the CPU time for computations with scaling – it depends on the task. If you decide not to "cut" the pictures at different dpi – put them in drawable-nodpi .

About caching or loading images asynchronously. There are quite a few different libs that solve this problem, for example universal-image-loader or the newfangled Picasso

Scroll to Top